diff --git a/Cargo.toml b/Cargo.toml index f2a5c2c858..b40e98413f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "kernel" version = "0.3.4" build = "build.rs" -edition = "2018" +edition = "2021" [lib] name = "kernel" @@ -44,7 +44,7 @@ raw-cpuid = "10.2.0" x86 = { version = "0.47.0", default-features = false } [features] -default = ["acpi", "multi_core", "graphical_debug", "serial_debug"] +default = ["acpi", "multi_core", "graphical_debug", "serial_debug", "x86_smap"] acpi = [] doc = [] graphical_debug = [] @@ -60,6 +60,9 @@ slab = ["slab_allocator"] # TODO: Either wait for LLVM 12 and use target_feature, or use another system for cpu features x86_fsgsbase = [] +# Enables SMAP if available, on x86_64. Ignored on other architectures. +x86_smap = [] + [profile.dev] # Avoids having to define the eh_personality lang item and reduces kernel size panic = "abort" diff --git a/linkers/aarch64.ld b/linkers/aarch64.ld index 0addd72004..cccc430c23 100644 --- a/linkers/aarch64.ld +++ b/linkers/aarch64.ld @@ -14,6 +14,9 @@ SECTIONS { *(.early_init.text*) . = ALIGN(4096); *(.text*) + __usercopy_start = .; + *(.usercopy-fns) + __usercopy_end = .; . = ALIGN(4096); __text_end = .; } diff --git a/linkers/i686.ld b/linkers/i686.ld index 6007098906..4579b5390f 100644 --- a/linkers/i686.ld +++ b/linkers/i686.ld @@ -12,6 +12,9 @@ SECTIONS { .text : AT(ADDR(.text) - KERNEL_OFFSET) { __text_start = .; *(.text*) + __usercopy_start = .; + *(.usercopy-fns) + __usercopy_end = .; . = ALIGN(4096); __text_end = .; } diff --git a/linkers/x86_64.ld b/linkers/x86_64.ld index 8b5c85e83a..35aadf19a9 100644 --- a/linkers/x86_64.ld +++ b/linkers/x86_64.ld @@ -12,6 +12,9 @@ SECTIONS { .text : AT(ADDR(.text) - KERNEL_OFFSET) { __text_start = .; *(.text*) + __usercopy_start = .; + *(.usercopy-fns) + __usercopy_end = .; . = ALIGN(4096); __text_end = .; } diff --git a/src/arch/aarch64/interrupt/exception.rs b/src/arch/aarch64/interrupt/exception.rs index cffceb5e68..dda39e751f 100644 --- a/src/arch/aarch64/interrupt/exception.rs +++ b/src/arch/aarch64/interrupt/exception.rs @@ -18,7 +18,40 @@ exception_stack!(synchronous_exception_at_el1_with_sp0, |stack| { loop {} }); +fn exception_code(esr: usize) -> u8 { + ((esr >> 26) & 0x3f) as u8 +} +fn iss(esr: usize) -> u32 { + (esr & 0x01ff_ffff) as u32 +} + exception_stack!(synchronous_exception_at_el1_with_spx, |stack| { + if exception_code(stack.iret.esr_el1) == 0b100101 { + // "Data Abort taken without a change in Exception level" + + let iss = iss(stack.iret.esr_el1); + + let was_translation_fault = iss >= 0b000100 && iss <= 0b000111; + let was_permission_fault = iss >= 0b001101 && iss <= 0b001111; + + extern "C" { + static __usercopy_start: u8; + static __usercopy_end: u8; + } + let usercopy = (&__usercopy_start as *const _ as usize)..(&__usercopy_end as *const _ as usize); + + if (was_translation_fault || was_permission_fault) && usercopy.contains(&{stack.iret.elr_el1}) { + // This was a usercopy page fault. Set the return value to nonzero to indicate usercopy + // failure (EFAULT), and emulate the return instruction by setting the return pointer + // to the saved LR value. + + stack.iret.elr_el1 = stack.preserved.x30; + stack.scratch.x0 = 1; + + return; + } + } + println!("Synchronous exception at EL1 with SPx"); stack.dump(); stack_trace(); @@ -27,8 +60,7 @@ exception_stack!(synchronous_exception_at_el1_with_spx, |stack| { exception_stack!(synchronous_exception_at_el0, |stack| { with_exception_stack!(|stack| { - let exception_code = (stack.iret.esr_el1 & (0x3f << 26)) >> 26; - if exception_code != 0b010101 { + if exception_code(stack.iret.esr_el1) != 0b010101 { println!("FATAL: Not an SVC induced synchronous exception"); stack.dump(); stack_trace(); diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index 0f353b390b..32504507cf 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -36,3 +36,29 @@ pub mod init; pub mod time; pub use ::rmm::AArch64Arch as CurrentRmmArch; + +pub use arch_copy_to_user as arch_copy_from_user; + +#[naked] +#[link_section = ".usercopy-fns"] +pub unsafe extern "C" fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 { + // x0, x1, x2 + core::arch::asm!(" + mov x4, x0 + mov x0, 0 + 2: + cmp x2, 0 + b.eq 3f + + ldrb w3, [x1] + strb w3, [x4] + + add x4, x4, 1 + add x1, x1, 1 + sub x2, x2, 1 + + b 2b + 3: + ret + ", options(noreturn)); +} diff --git a/src/arch/aarch64/rmm.rs b/src/arch/aarch64/rmm.rs index 5fed354e88..059cc1fe49 100644 --- a/src/arch/aarch64/rmm.rs +++ b/src/arch/aarch64/rmm.rs @@ -324,6 +324,11 @@ impl core::ops::Deref for KernelMapper { &self.mapper } } +impl core::ops::DerefMut for KernelMapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.mapper + } +} impl Drop for KernelMapper { fn drop(&mut self) { if LOCK_COUNT.fetch_sub(1, Ordering::Relaxed) == 1 { diff --git a/src/arch/x86/interrupt/exception.rs b/src/arch/x86/interrupt/exception.rs index c01ae15f7c..6331314f74 100644 --- a/src/arch/x86/interrupt/exception.rs +++ b/src/arch/x86/interrupt/exception.rs @@ -1,3 +1,6 @@ +use rmm::TableKind; +use x86::irq::PageFaultError; + use crate::{ interrupt::stack_trace, ptrace, @@ -130,15 +133,57 @@ interrupt_error!(protection, |stack| { ksignal(SIGSEGV); }); +#[naked] +unsafe extern "C" fn usercopy_trampoline() { + core::arch::asm!(" + mov eax, 1 + + pop esi + pop edi + + ret 4 + ", options(noreturn)); +} + interrupt_error!(page, |stack| { - let cr2: usize; - core::arch::asm!("mov {}, cr2", out(reg) cr2); + // TODO: Share code with x86_64? + let cr2 = unsafe { x86::controlregs::cr2() }; + let flags = PageFaultError::from_bits_truncate(stack.code as u32); + + extern "C" { + static __usercopy_start: u8; + static __usercopy_end: u8; + } + let usercopy_region = (&__usercopy_start as *const u8 as usize)..(&__usercopy_end as *const u8 as usize); + + // TODO: Most likely not necessary, but maybe also check that cr2 is not too close to USER_END. + let address_is_user = rmm::VirtualAddress::new(cr2).kind() == TableKind::User; + + let invalid_page_tables = flags.contains(PageFaultError::RSVD); + let caused_by_user = flags.contains(PageFaultError::US); + let caused_by_instr_fetch = flags.contains(PageFaultError::ID); + + if address_is_user && !caused_by_user && !caused_by_instr_fetch && !invalid_page_tables && usercopy_region.contains(&{ stack.inner.iret.eip }) { + // Unlike on x86_64, Protected Mode interrupts will not save/restore esp and ss unless + // privilege rings changed, which they won't here as we are catching a kernel-induced page + // fault. + // + // Thus, it is only possible to change scratch/preserved registers, and EIP. While it may + // be feasible to set ECX to zero to stop the REP MOVSB, or increase EIP by 2 (REP MOVSB is + // f3 a4, i.e. 2 bytes), this trampoline allows any memcpy implementation, that reasonably + // pushes preserved registers to the stack. + stack.inner.iret.eip = usercopy_trampoline as usize; + + return; + } + println!("Page fault: {:>016X}", cr2); - println!(" Present: {}", stack.code & 1 << 0 != 0); - println!(" Write: {}", stack.code & 1 << 1 != 0); - println!(" User: {}", stack.code & 1 << 2 != 0); - println!(" Reserved write: {}", stack.code & 1 << 3 != 0); - println!(" Instruction fetch: {}", stack.code & 1 << 4 != 0); + println!(" Present: {}", flags.contains(PageFaultError::P)); + println!(" Write: {}", flags.contains(PageFaultError::WR)); + println!(" User: {}", flags.contains(PageFaultError::US)); + println!(" Reserved write: {}", flags.contains(PageFaultError::RSVD)); + println!(" Instruction fetch: {}", flags.contains(PageFaultError::ID)); + stack.dump(); stack_trace(); ksignal(SIGSEGV); diff --git a/src/arch/x86/interrupt/handler.rs b/src/arch/x86/interrupt/handler.rs index 358e2322f4..07710653d8 100644 --- a/src/arch/x86/interrupt/handler.rs +++ b/src/arch/x86/interrupt/handler.rs @@ -14,9 +14,9 @@ pub struct ScratchRegisters { impl ScratchRegisters { pub fn dump(&self) { - println!("EAX: {:016x}", { self.eax }); - println!("ECX: {:016x}", { self.ecx }); - println!("EDX: {:016x}", { self.edx }); + println!("EAX: {:08x}", { self.eax }); + println!("ECX: {:08x}", { self.ecx }); + println!("EDX: {:08x}", { self.edx }); } } @@ -31,10 +31,10 @@ pub struct PreservedRegisters { impl PreservedRegisters { pub fn dump(&self) { - println!("EBX: {:016x}", { self.ebx }); - println!("EDI: {:016x}", { self.edi }); - println!("ESI: {:016x}", { self.esi }); - println!("EBP: {:016x}", { self.ebp }); + println!("EBX: {:08x}", { self.ebx }); + println!("EDI: {:08x}", { self.edi }); + println!("ESI: {:08x}", { self.esi }); + println!("EBP: {:08x}", { self.ebp }); } } @@ -56,13 +56,13 @@ pub struct IretRegisters { impl IretRegisters { pub fn dump(&self) { - println!("EFLAG: {:016x}", { self.eflags }); - println!("CS: {:016x}", { self.cs }); - println!("EIP: {:016x}", { self.eip }); + println!("EFLAG: {:08x}", { self.eflags }); + println!("CS: {:08x}", { self.cs }); + println!("EIP: {:08x}", { self.eip }); if self.cs & 0b11 != 0b00 { - println!("ESP: {:016x}", { self.esp }); - println!("SS: {:016x}", { self.ss }); + println!("ESP: {:08x}", { self.esp }); + println!("SS: {:08x}", { self.ss }); } } } @@ -161,7 +161,7 @@ pub struct InterruptErrorStack { impl InterruptErrorStack { pub fn dump(&self) { - println!("CODE: {:016x}", { self.code }); + println!("CODE: {:08x}", { self.code }); self.inner.dump(); } } @@ -378,13 +378,13 @@ macro_rules! interrupt_error { " push esp call {inner} - pop esp ", + // add esp, 4 - // TODO: Unmap PTI + // TODO: Unmap PTI (split "add esp, 8" into two "add esp, 4"s maybe?) // $crate::arch::x86::pti::unmap(); - // Pop code + // Pop previous esp and code "add esp, 8\n", // Exit kernel TLS segment diff --git a/src/arch/x86/mod.rs b/src/arch/x86/mod.rs index 4c294ef1b3..9d68c43ec2 100644 --- a/src/arch/x86/mod.rs +++ b/src/arch/x86/mod.rs @@ -50,3 +50,34 @@ pub mod flags { pub const FLAG_SINGLESTEP: usize = 1 << SHIFT_SINGLESTEP; pub const FLAG_INTERRUPTS: usize = 1 << 9; } +pub use arch_copy_to_user as arch_copy_from_user; + +#[inline(always)] +pub unsafe fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 { + arch_copy_to_user_inner(len, dst, src) +} + +#[naked] +#[link_section = ".usercopy-fns"] +#[no_mangle] +pub unsafe extern "fastcall" fn arch_copy_to_user_inner(len: usize, dst: usize, src: usize) -> u8 { + // Explicitly specified __fastcall ABI: + // + // ECX = len, EDX = dst, src is pushed to the stack (earlier than the CALL return address, of + // course) + core::arch::asm!(" + push edi + push esi + + mov edi, edx + mov esi, [esp+12] + + xor eax, eax + rep movsb + + pop esi + pop edi + + ret 4 + ", options(noreturn)); +} diff --git a/src/arch/x86/rmm.rs b/src/arch/x86/rmm.rs index 660fe7c3d9..4593c15977 100644 --- a/src/arch/x86/rmm.rs +++ b/src/arch/x86/rmm.rs @@ -322,6 +322,11 @@ impl core::ops::Deref for KernelMapper { &self.mapper } } +impl core::ops::DerefMut for KernelMapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.mapper + } +} impl Drop for KernelMapper { fn drop(&mut self) { if LOCK_COUNT.fetch_sub(1, Ordering::Relaxed) == 1 { diff --git a/src/arch/x86_64/cpuid.rs b/src/arch/x86_64/cpuid.rs index cb1eaa456a..6562b03919 100644 --- a/src/arch/x86_64/cpuid.rs +++ b/src/arch/x86_64/cpuid.rs @@ -1,8 +1,11 @@ use raw_cpuid::{CpuId, CpuIdResult}; pub fn cpuid() -> Option { - //TODO: ensure that CPUID exists! https://wiki.osdev.org/CPUID#Checking_CPUID_availability - Some(CpuId::with_cpuid_fn(|a, c| { + // CPUID is always available on x86_64 systems. + Some(cpuid_always()) +} +pub fn cpuid_always() -> CpuId { + CpuId::with_cpuid_fn(|a, c| { let result = unsafe { core::arch::x86_64::__cpuid_count(a, c) }; CpuIdResult { eax: result.eax, @@ -10,5 +13,5 @@ pub fn cpuid() -> Option { ecx: result.ecx, edx: result.edx, } - })) + }) } diff --git a/src/arch/x86_64/interrupt/exception.rs b/src/arch/x86_64/interrupt/exception.rs index 142e2b5d76..d67ef9fc02 100644 --- a/src/arch/x86_64/interrupt/exception.rs +++ b/src/arch/x86_64/interrupt/exception.rs @@ -1,5 +1,9 @@ +use rmm::TableKind; +use x86::irq::PageFaultError; + use crate::{ interrupt::stack_trace, + paging::VirtualAddress, ptrace, syscall::flag::*, @@ -131,14 +135,39 @@ interrupt_error!(protection, |stack| { }); interrupt_error!(page, |stack| { - let cr2: usize; - core::arch::asm!("mov {}, cr2", out(reg) cr2); + let cr2 = unsafe { x86::controlregs::cr2() }; + let flags = PageFaultError::from_bits_truncate(stack.code as u32); + + extern "C" { + static __usercopy_start: u8; + static __usercopy_end: u8; + } + let usercopy_region = (&__usercopy_start as *const u8 as usize)..(&__usercopy_end as *const u8 as usize); + + // TODO: Most likely not necessary, but maybe also check that cr2 is not too close to USER_END. + let address_is_user = VirtualAddress::new(cr2).kind() == TableKind::User; + + let invalid_page_tables = flags.contains(PageFaultError::RSVD); + let caused_by_user = flags.contains(PageFaultError::US); + let caused_by_instr_fetch = flags.contains(PageFaultError::ID); + + if address_is_user && !caused_by_user && !caused_by_instr_fetch && !invalid_page_tables && usercopy_region.contains(&{ stack.inner.iret.rip }) { + // We were inside a usercopy function that failed. This is handled by setting rax to a + // nonzero value, and emulating the ret instruction. + stack.inner.scratch.rax = 1; + let ret_addr = unsafe { (stack.inner.iret.rsp as *const usize).read() }; + stack.inner.iret.rsp += 8; + stack.inner.iret.rip = ret_addr; + stack.inner.iret.rflags &= !(1 << 18); + return; + } + println!("Page fault: {:>016X}", cr2); - println!(" Present: {}", stack.code & 1 << 0 != 0); - println!(" Write: {}", stack.code & 1 << 1 != 0); - println!(" User: {}", stack.code & 1 << 2 != 0); - println!(" Reserved write: {}", stack.code & 1 << 3 != 0); - println!(" Instruction fetch: {}", stack.code & 1 << 4 != 0); + println!(" Present: {}", flags.contains(PageFaultError::P)); + println!(" Write: {}", flags.contains(PageFaultError::WR)); + println!(" User: {}", flags.contains(PageFaultError::US)); + println!(" Reserved write: {}", flags.contains(PageFaultError::RSVD)); + println!(" Instruction fetch: {}", flags.contains(PageFaultError::ID)); stack.dump(); stack_trace(); ksignal(SIGSEGV); diff --git a/src/arch/x86_64/interrupt/trace.rs b/src/arch/x86_64/interrupt/trace.rs index 31c6812c8c..938f6aa4d3 100644 --- a/src/arch/x86_64/interrupt/trace.rs +++ b/src/arch/x86_64/interrupt/trace.rs @@ -3,7 +3,7 @@ use core::{mem, str}; use goblin::elf::sym; use rustc_demangle::demangle; -use crate::{paging::{KernelMapper, VirtualAddress}}; +use crate::{paging::{KernelMapper, VirtualAddress}, USER_END_OFFSET}; /// Get a stack trace //TODO: Check for stack being mapped before dereferencing @@ -21,14 +21,14 @@ pub unsafe fn stack_trace() { if let Some(rip_rbp) = rbp.checked_add(mem::size_of::()) { let rbp_virt = VirtualAddress::new(rbp); let rip_rbp_virt = VirtualAddress::new(rip_rbp); - if rbp_virt.is_canonical() && rip_rbp_virt.is_canonical() && mapper.translate(rbp_virt).is_some() && mapper.translate(rip_rbp_virt).is_some() { - let rip = *(rip_rbp as *const usize); + if rbp_virt.data() >= USER_END_OFFSET && rip_rbp_virt.data() >= USER_END_OFFSET && mapper.translate(rbp_virt).is_some() && mapper.translate(rip_rbp_virt).is_some() { + let rip = (rip_rbp as *const usize).read(); if rip == 0 { println!(" {:>016X}: EMPTY RETURN", rbp); break; } println!(" {:>016X}: {:>016X}", rbp, rip); - rbp = *(rbp as *const usize); + rbp = (rbp as *const usize).read(); symbol_trace(rip); } else { println!(" {:>016X}: GUARD PAGE", rbp); diff --git a/src/arch/x86_64/misc.rs b/src/arch/x86_64/misc.rs new file mode 100644 index 0000000000..733e42eb78 --- /dev/null +++ b/src/arch/x86_64/misc.rs @@ -0,0 +1,28 @@ +use x86::controlregs::Cr4; +use x86::cpuid::ExtendedFeatures; + +pub unsafe fn init() { + let has_ext_feat = |feat: fn(ExtendedFeatures) -> bool| crate::cpuid::cpuid_always().get_extended_feature_info().map_or(false, feat); + + if has_ext_feat(|feat| feat.has_umip()) { + // UMIP (UserMode Instruction Prevention) forbids userspace from calling SGDT, SIDT, SLDT, + // SMSW and STR. KASLR is currently not implemented, but this protects against leaking + // addresses. + x86::controlregs::cr4_write(x86::controlregs::cr4() | Cr4::CR4_ENABLE_UMIP); + } + if has_ext_feat(|feat| feat.has_smep()) { + // SMEP (Supervisor-Mode Execution Prevention) forbids the kernel from executing + // instruction on any page marked "userspace-accessible". This improves security for + // obvious reasons. + x86::controlregs::cr4_write(x86::controlregs::cr4() | Cr4::CR4_ENABLE_SMEP); + } + if has_ext_feat(|feat| feat.has_smap()) && cfg!(feature = "x86_smap") { + // SMAP (Supervisor-Mode Access Prevention) forbids the kernel from accessing any + // userspace-accessible pages, with the necessary exception of RFLAGS.AC = 1. This limits + // user-memory accesses to the UserSlice wrapper, so that no code outside of usercopy + // functions can be accidentally accessed by the kernel. + x86::controlregs::cr4_write(x86::controlregs::cr4() | Cr4::CR4_ENABLE_SMAP); + // Clear CLAC in (the probably unlikely) case the bootloader set it earlier. + x86::bits64::rflags::clac(); + } +} diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index ca21bb516c..860513cd22 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -26,6 +26,9 @@ pub mod idt; /// Inter-processor interrupts pub mod ipi; +/// Miscellaneous processor features +pub mod misc; + /// Paging pub mod paging; @@ -50,3 +53,28 @@ pub mod flags { pub const FLAG_SINGLESTEP: usize = 1 << SHIFT_SINGLESTEP; pub const FLAG_INTERRUPTS: usize = 1 << 9; } + +#[naked] +#[link_section = ".usercopy-fns"] +pub unsafe extern "C" fn arch_copy_to_user(dst: usize, src: usize, len: usize) -> u8 { + // TODO: LFENCE (spectre_v1 mitigation)? + + #[cfg(not(feature = "x86_smap"))] + core::arch::asm!(" + xor eax, eax + mov rcx, rdx + rep movsb + ret + ", options(noreturn)); + + #[cfg(feature = "x86_smap")] + core::arch::asm!(" + xor eax, eax + mov rcx, rdx + stac + rep movsb + clac + ret + ", options(noreturn)); +} +pub use arch_copy_to_user as arch_copy_from_user; diff --git a/src/arch/x86_64/paging/mod.rs b/src/arch/x86_64/paging/mod.rs index 336469bfa1..aebe64b4ee 100644 --- a/src/arch/x86_64/paging/mod.rs +++ b/src/arch/x86_64/paging/mod.rs @@ -253,9 +253,9 @@ impl Iterator for PageIter { /// Round down to the nearest multiple of page size pub fn round_down_pages(number: usize) -> usize { - number - number % PAGE_SIZE + number.div_floor(PAGE_SIZE) * PAGE_SIZE } /// Round up to the nearest multiple of page size pub fn round_up_pages(number: usize) -> usize { - round_down_pages(number + PAGE_SIZE - 1) + number.next_multiple_of(PAGE_SIZE) } diff --git a/src/arch/x86_64/rmm.rs b/src/arch/x86_64/rmm.rs index 830e9d3c21..fd10673b18 100644 --- a/src/arch/x86_64/rmm.rs +++ b/src/arch/x86_64/rmm.rs @@ -313,6 +313,11 @@ impl core::ops::Deref for KernelMapper { &self.mapper } } +impl core::ops::DerefMut for KernelMapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.mapper + } +} impl Drop for KernelMapper { fn drop(&mut self) { if LOCK_COUNT.fetch_sub(1, Ordering::Relaxed) == 1 { diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 92e3bbcac0..9f5096dd69 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -17,6 +17,7 @@ use crate::devices::graphical_debug; use crate::gdt; use crate::idt; use crate::interrupt; +use crate::misc; use crate::log::{self, info}; use crate::paging::{self, KernelMapper, TableKind}; @@ -169,6 +170,9 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Activate memory logging log::init(); + // Initialize miscellaneous processor features + misc::init(); + // Initialize devices device::init(); @@ -247,6 +251,9 @@ pub unsafe extern fn kstart_ap(args_ptr: *const KernelArgsAp) -> ! { // Set up syscall instruction interrupt::syscall::init(); + // Initialize miscellaneous processor features + misc::init(); + // Test tdata and tbss { assert_eq!(TBSS_TEST_ZERO, 0); diff --git a/src/common/aligned_box.rs b/src/common/aligned_box.rs new file mode 100644 index 0000000000..3c1cdd68ff --- /dev/null +++ b/src/common/aligned_box.rs @@ -0,0 +1,77 @@ +use core::{alloc::GlobalAlloc, mem}; + +use crate::common::unique::Unique; +use crate::memory::Enomem; + +// Necessary because GlobalAlloc::dealloc requires the layout to be the same, and therefore Box +// cannot be used for increased alignment directly. +// TODO: move to common? +pub struct AlignedBox { + inner: Unique, +} +pub unsafe trait ValidForZero {} +unsafe impl ValidForZero for [u8; N] {} + +unsafe impl ValidForZero for crate::syscall::data::Stat {} +unsafe impl ValidForZero for crate::syscall::data::StatVfs {} + +impl AlignedBox { + const LAYOUT: core::alloc::Layout = { + const fn max(a: usize, b: usize) -> usize { + if a > b { a } else { b } + } + + match core::alloc::Layout::from_size_align(mem::size_of::(), max(mem::align_of::(), ALIGN)) { + Ok(l) => l, + Err(_) => panic!("layout validation failed at compile time"), + } + }; + #[inline(always)] + pub fn try_zeroed() -> Result + where + T: ValidForZero, + { + Ok(unsafe { + let ptr = crate::ALLOCATOR.alloc_zeroed(Self::LAYOUT); + if ptr.is_null() { + return Err(Enomem); + } + Self { + inner: Unique::new_unchecked(ptr.cast()), + } + }) + } +} + +impl core::fmt::Debug for AlignedBox { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "[aligned box at {:p}, size {} alignment {}]", self.inner.as_ptr(), mem::size_of::(), mem::align_of::()) + } +} +impl Drop for AlignedBox { + fn drop(&mut self) { + unsafe { + core::ptr::drop_in_place(self.inner.as_ptr()); + crate::ALLOCATOR.dealloc(self.inner.as_ptr().cast(), Self::LAYOUT); + } + } +} +impl core::ops::Deref for AlignedBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.inner.as_ptr() } + } +} +impl core::ops::DerefMut for AlignedBox { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.inner.as_ptr() } + } +} +impl Clone for AlignedBox { + fn clone(&self) -> Self { + let mut new = Self::try_zeroed().unwrap_or_else(|_| alloc::alloc::handle_alloc_error(Self::LAYOUT)); + T::clone_from(&mut new, self); + new + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 4bdafba1f5..9f2a70f77c 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1,3 +1,4 @@ +pub mod aligned_box; #[macro_use] pub mod int_like; pub mod unique; diff --git a/src/context/context.rs b/src/context/context.rs index c2a5a6421a..0cc113d08f 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -5,24 +5,23 @@ use alloc::{ vec::Vec, borrow::Cow, }; use core::{ - alloc::GlobalAlloc, cmp::Ordering, mem, }; use spin::RwLock; use crate::arch::{interrupt::InterruptStack, paging::PAGE_SIZE}; +use crate::common::aligned_box::AlignedBox; use crate::common::unique::Unique; -use crate::context::arch; +use crate::context::{self, arch}; use crate::context::file::{FileDescriptor, FileDescription}; use crate::context::memory::AddrSpace; use crate::ipi::{ipi, IpiKind, IpiTarget}; -use crate::memory::Enomem; use crate::scheme::{SchemeNamespace, FileHandle}; use crate::sync::WaitMap; use crate::syscall::data::SigAction; -use crate::syscall::error::{Result, Error, ESRCH}; +use crate::syscall::error::{Result, Error, EAGAIN, EINVAL, ESRCH}; use crate::syscall::flag::{SIG_DFL, SigActionFlags}; /// Unique identifier for a context (i.e. `pid`). @@ -212,9 +211,11 @@ pub struct Context { /// Current system call pub syscall: Option<(usize, usize, usize, usize, usize, usize)>, /// Head buffer to use when system call buffers are not page aligned - pub syscall_head: AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>, + // TODO: Store in user memory? + pub syscall_head: Option>, /// Tail buffer to use when system call buffers are not page aligned - pub syscall_tail: AlignedBox<[u8; PAGE_SIZE], PAGE_SIZE>, + // TODO: Store in user memory? + pub syscall_tail: Option>, /// Context is halting parent pub vfork: bool, /// Context is being waited on @@ -265,81 +266,8 @@ pub struct Context { pub clone_entry: Option<[usize; 2]>, } -// Necessary because GlobalAlloc::dealloc requires the layout to be the same, and therefore Box -// cannot be used for increased alignment directly. -// TODO: move to common? -pub struct AlignedBox { - inner: Unique, -} -pub unsafe trait ValidForZero {} -unsafe impl ValidForZero for [u8; N] {} - -impl AlignedBox { - const LAYOUT: core::alloc::Layout = { - const fn max(a: usize, b: usize) -> usize { - if a > b { a } else { b } - } - - match core::alloc::Layout::from_size_align(mem::size_of::(), max(mem::align_of::(), ALIGN)) { - Ok(l) => l, - Err(_) => panic!("layout validation failed at compile time"), - } - }; - #[inline(always)] - pub fn try_zeroed() -> Result - where - T: ValidForZero, - { - Ok(unsafe { - let ptr = crate::ALLOCATOR.alloc_zeroed(Self::LAYOUT); - if ptr.is_null() { - return Err(Enomem); - } - Self { - inner: Unique::new_unchecked(ptr.cast()), - } - }) - } -} - -impl core::fmt::Debug for AlignedBox { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "[aligned box at {:p}, size {} alignment {}]", self.inner.as_ptr(), mem::size_of::(), mem::align_of::()) - } -} -impl Drop for AlignedBox { - fn drop(&mut self) { - unsafe { - core::ptr::drop_in_place(self.inner.as_ptr()); - crate::ALLOCATOR.dealloc(self.inner.as_ptr().cast(), Self::LAYOUT); - } - } -} -impl core::ops::Deref for AlignedBox { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.inner.as_ptr() } - } -} -impl core::ops::DerefMut for AlignedBox { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.inner.as_ptr() } - } -} -impl Clone for AlignedBox { - fn clone(&self) -> Self { - let mut new = Self::try_zeroed().unwrap_or_else(|_| alloc::alloc::handle_alloc_error(Self::LAYOUT)); - T::clone_from(&mut new, self); - new - } -} - impl Context { pub fn new(id: ContextId) -> Result { - let syscall_head = AlignedBox::try_zeroed()?; - let syscall_tail = AlignedBox::try_zeroed()?; - let this = Context { id, pgid: id, @@ -360,8 +288,8 @@ impl Context { cpu_time: 0, sched_affinity: None, syscall: None, - syscall_head, - syscall_tail, + syscall_head: Some(AlignedBox::try_zeroed()?), + syscall_tail: Some(AlignedBox::try_zeroed()?), vfork: false, waitpid: Arc::new(WaitMap::new()), pending: VecDeque::new(), @@ -505,3 +433,65 @@ impl Context { ); 128])) } } + +/// Wrapper struct for borrowing the syscall head or tail buf. +#[derive(Debug)] +pub struct BorrowedHtBuf { + inner: Option>, + head_and_not_tail: bool, +} +impl BorrowedHtBuf { + pub fn head() -> Result { + Ok(Self { + inner: Some(context::current()?.write().syscall_head.take().ok_or(Error::new(EAGAIN))?), + head_and_not_tail: true, + }) + } + pub fn tail() -> Result { + Ok(Self { + inner: Some(context::current()?.write().syscall_tail.take().ok_or(Error::new(EAGAIN))?), + head_and_not_tail: false, + }) + } + pub fn buf(&self) -> &[u8; PAGE_SIZE] { + self.inner.as_ref().expect("must succeed") + } + pub fn buf_mut(&mut self) -> &mut [u8; PAGE_SIZE] { + self.inner.as_mut().expect("must succeed") + } + /* + pub fn use_for_slice(&mut self, raw: UserSlice) -> Result> { + if raw.len() > self.buf().len() { + return Ok(None); + } + raw.copy_to_slice(&mut self.buf_mut()[..raw.len()])?; + Ok(Some(&self.buf()[..raw.len()])) + } + pub fn use_for_string(&mut self, raw: UserSlice) -> Result<&str> { + let slice = self.use_for_slice(raw)?.ok_or(Error::new(ENAMETOOLONG))?; + core::str::from_utf8(slice).map_err(|_| Error::new(EINVAL)) + } + */ + pub unsafe fn use_for_struct(&mut self) -> Result<&mut T> { + if mem::size_of::() > PAGE_SIZE || mem::align_of::() > PAGE_SIZE { + return Err(Error::new(EINVAL)); + } + self.buf_mut().fill(0_u8); + Ok(unsafe { &mut *self.buf_mut().as_mut_ptr().cast() }) + } +} +impl Drop for BorrowedHtBuf { + fn drop(&mut self) { + let Ok(context) = context::current() else { + return; + }; + let Some(inner) = self.inner.take() else { + return; + }; + match context.write() { + mut context => { + (if self.head_and_not_tail { &mut context.syscall_head } else { &mut context.syscall_tail }).get_or_insert(inner); + } + } + } +} diff --git a/src/context/memory.rs b/src/context/memory.rs index 95251da51a..e56f97eedb 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -693,7 +693,7 @@ impl Grant { (entry, entry_flags) } else { - src_mapper.translate(src_page.start_address()).expect("grant references unmapped memory") + src_mapper.translate(src_page.start_address()).unwrap_or_else(|| panic!("grant at {:p} references unmapped memory", src_page.start_address().data() as *const u8)) }; let flush = match unsafe { dst_mapper.map_phys(dst_base.next_by(index).start_address(), address, flags) } { @@ -829,8 +829,6 @@ impl Grant { Some((before_grant, self, after_grant)) } - // FIXME - /* pub fn can_be_merged_if_adjacent(&self, with: &Self) -> bool { match (&self.desc_opt, &with.desc_opt) { (None, None) => (), @@ -840,7 +838,6 @@ impl Grant { } self.owned == with.owned && self.mapped == with.mapped && self.flags.data() == with.flags.data() } - */ } impl Deref for Grant { diff --git a/src/context/mod.rs b/src/context/mod.rs index 5a00526fca..e08bd71fc8 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -11,7 +11,7 @@ use spin::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::paging::{RmmA, RmmArch, TableKind}; use crate::syscall::error::{Error, ESRCH, Result}; -pub use self::context::{Context, ContextId, ContextSnapshot, Status, WaitpidKey}; +pub use self::context::{BorrowedHtBuf, Context, ContextId, ContextSnapshot, Status, WaitpidKey}; pub use self::list::ContextList; pub use self::switch::switch; diff --git a/src/context/signal.rs b/src/context/signal.rs index 7b19831e73..2497d43a83 100644 --- a/src/context/signal.rs +++ b/src/context/signal.rs @@ -6,6 +6,7 @@ use syscall::ptrace_event; use crate::context::{contexts, switch, Status, WaitpidKey}; use crate::start::usermode; use crate::ptrace; +use crate::syscall::usercopy::UserSlice; pub fn is_user_handled(handler: Option) -> bool { let handler = handler.map(|ptr| ptr as usize).unwrap_or(0); @@ -115,14 +116,19 @@ pub extern "C" fn signal_handler(sig: usize) { }; unsafe { - let mut sp = sigstack.expect("sigaction was set while sigstack was not") - 256; + const REDZONE_SIZE: usize = 256; + let mut sp = sigstack.expect("sigaction was set while sigstack was not") - REDZONE_SIZE; sp = (sp / 16) * 16; sp -= mem::size_of::(); - *(sp as *mut usize) = restorer; - usermode(handler, sp, sig, usize::from(singlestep)); + match UserSlice::wo(sp, core::mem::size_of::()).and_then(|buf| buf.write_usize(restorer)) { + Ok(()) => usermode(handler, sp, sig, usize::from(singlestep)), + Err(error) => { + log::error!("Failed to signal: {}", error); + } + } } } diff --git a/src/debugger.rs b/src/debugger.rs index 3dc6fdd2e0..4a97f7f560 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -153,6 +153,8 @@ pub unsafe fn debugger(target_id: Option) { // Super unsafe due to page table switching and raw pointers! #[cfg(target_arch = "x86_64")] pub unsafe fn debugger(target_id: Option) { + unsafe { x86::bits64::rflags::stac(); } + println!("DEBUGGER START"); println!(); @@ -221,6 +223,7 @@ pub unsafe fn debugger(target_id: Option) { } println!("DEBUGGER END"); + unsafe { x86::bits64::rflags::clac(); } } #[cfg(target_arch = "x86_64")] diff --git a/src/event.rs b/src/event.rs index f55591cb17..dbef4bfa17 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,8 +7,9 @@ use crate::context; use crate::scheme::{self, SchemeId}; use crate::sync::WaitQueue; use crate::syscall::data::Event; -use crate::syscall::error::{Error, Result, EBADF, EINTR, ESRCH}; +use crate::syscall::error::{Error, Result, EBADF, ESRCH}; use crate::syscall::flag::EventFlags; +use crate::syscall::usercopy::UserSliceWo; int_like!(EventQueueId, AtomicEventQueueId, usize, AtomicUsize); @@ -25,8 +26,8 @@ impl EventQueue { } } - pub fn read(&self, events: &mut [Event]) -> Result { - self.queue.receive_into(events, true, "EventQueue::read").ok_or(Error::new(EINTR)) + pub fn read(&self, buf: UserSliceWo) -> Result { + self.queue.receive_into_user(buf, true, "EventQueue::read") } pub fn write(&self, events: &[Event]) -> Result { diff --git a/src/lib.rs b/src/lib.rs index 5bde061a31..3d3db8ef96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,13 +46,16 @@ #![feature(allocator_api)] #![feature(arbitrary_self_types)] #![feature(array_chunks)] +#![feature(iter_array_chunks)] #![feature(asm_const)] // TODO: Relax requirements of most asm invocations #![feature(concat_idents)] #![feature(core_intrinsics)] #![feature(integer_atomics)] +#![feature(int_roundings)] #![feature(naked_functions)] #![feature(ptr_internals)] #![feature(slice_ptr_get, slice_ptr_len)] +#![feature(sync_unsafe_cell)] #![feature(thread_local)] #![no_std] diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index f9bc7380fe..1800f40e53 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -22,6 +22,7 @@ use crate::syscall::flag::{ }; use crate::syscall::scheme::Scheme; use crate::syscall::error::{Error, Result}; +use crate::syscall::usercopy::{UserSliceRo, UserSliceWo}; /// A scheme used to access the RSDT or XSDT, which is needed for e.g. `acpid` to function. pub struct AcpiScheme; @@ -162,29 +163,6 @@ impl Scheme for AcpiScheme { Ok(fd) } - fn fstat(&self, id: usize, stat: &mut Stat) -> Result { - let handles = HANDLES.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - - match handle.kind { - HandleKind::Rxsdt => { - let data = DATA.get().ok_or(Error::new(EBADFD))?; - - stat.st_mode = MODE_FILE; - stat.st_size = data.len().try_into().unwrap_or(u64::max_value()); - } - HandleKind::TopLevel => { - stat.st_mode = MODE_DIR; - stat.st_size = TOPLEVEL_CONTENTS.len().try_into().unwrap_or(u64::max_value()); - } - HandleKind::ShutdownPipe => { - stat.st_mode = MODE_CHR; - stat.st_size = 1; - } - } - - Ok(0) - } fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -218,7 +196,29 @@ impl Scheme for AcpiScheme { Ok(new_offset as isize) } - fn read(&self, id: usize, dst_buf: &mut [u8]) -> Result { + // TODO + fn fevent(&self, id: usize, _flags: EventFlags) -> Result { + let handles = HANDLES.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + if handle.stat { + return Err(Error::new(EBADF)); + } + + Ok(EventFlags::empty()) + } + fn close(&self, id: usize) -> Result { + if HANDLES.write().remove(&id).is_none() { + return Err(Error::new(EBADF)); + } + Ok(0) + } +} +impl crate::scheme::KernelScheme for AcpiScheme { + fn kwrite(&self, _id: usize, _buf: UserSliceRo) -> Result { + Err(Error::new(EBADF)) + } + fn kread(&self, id: usize, dst_buf: UserSliceWo) -> Result { let mut handles = HANDLES.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -228,14 +228,9 @@ impl Scheme for AcpiScheme { let data = match handle.kind { HandleKind::ShutdownPipe => { - let dst_byte = match dst_buf.first_mut() { - None => return Ok(0), - Some(dst) => if handle.offset >= 1 { - return Ok(0) - } else { - dst - }, - }; + if dst_buf.is_empty() { + return Ok(0); + } loop { let flag_guard = KSTOP_FLAG.lock(); @@ -247,7 +242,7 @@ impl Scheme for AcpiScheme { } } - *dst_byte = 0x42; + dst_buf.copy_exactly(&[0x42])?; handle.offset = 1; return Ok(1); } @@ -260,32 +255,37 @@ impl Scheme for AcpiScheme { .get(src_offset..) .expect("expected data to be at least data.len() bytes long"); - let bytes_to_copy = core::cmp::min(dst_buf.len(), src_buf.len()); + let bytes_copied = dst_buf.copy_common_bytes_from_slice(src_buf)?; + handle.offset += bytes_copied; - dst_buf[..bytes_to_copy].copy_from_slice(&src_buf[..bytes_to_copy]); - handle.offset += bytes_to_copy; - - Ok(bytes_to_copy) + Ok(bytes_copied) } - // TODO - fn fevent(&self, id: usize, _flags: EventFlags) -> Result { + fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result { let handles = HANDLES.read(); let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - if handle.stat { - return Err(Error::new(EBADF)); - } + buf.copy_exactly(&match handle.kind { + HandleKind::Rxsdt => { + let data = DATA.get().ok_or(Error::new(EBADFD))?; + + Stat { + st_mode: MODE_FILE, + st_size: data.len().try_into().unwrap_or(u64::max_value()), + ..Default::default() + } + }, + HandleKind::TopLevel => Stat { + st_mode: MODE_DIR, + st_size: TOPLEVEL_CONTENTS.len().try_into().unwrap_or(u64::max_value()), + ..Default::default() + }, + HandleKind::ShutdownPipe => Stat { + st_mode: MODE_CHR, + st_size: 1, + ..Default::default() + }, + })?; - Ok(EventFlags::empty()) - } - fn write(&self, _id: usize, _buf: &[u8]) -> Result { - Err(Error::new(EBADF)) - } - fn close(&self, id: usize) -> Result { - if HANDLES.write().remove(&id).is_none() { - return Err(Error::new(EBADF)); - } Ok(0) } } -impl crate::scheme::KernelScheme for AcpiScheme {} diff --git a/src/scheme/debug.rs b/src/scheme/debug.rs index 9fd1739ce5..d794200c86 100644 --- a/src/scheme/debug.rs +++ b/src/scheme/debug.rs @@ -7,6 +7,8 @@ use crate::scheme::*; use crate::sync::WaitQueue; use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK}; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::UserSliceRo; +use crate::syscall::usercopy::UserSliceWo; static SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default(); @@ -78,33 +80,6 @@ impl Scheme for DebugScheme { Ok(id) } - /// Read the file `number` into the `buffer` - /// - /// Returns the number of bytes read - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let handle = { - let handles = handles(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - INPUT.call_once(init_input) - .receive_into(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "DebugScheme::read") - .ok_or(Error::new(EINTR)) - } - - /// Write the `buffer` to the `file` - /// - /// Returns the number of bytes written - fn write(&self, id: usize, buf: &[u8]) -> Result { - let _handle = { - let handles = handles(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - Writer::new().write(buf); - Ok(buf.len()) - } - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { let mut handles = handles_mut(); if let Some(handle) = handles.get_mut(&id) { @@ -130,22 +105,6 @@ impl Scheme for DebugScheme { Ok(EventFlags::empty()) } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let _handle = { - let handles = handles(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let mut i = 0; - let scheme_path = b"debug:"; - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - - Ok(i) - } - fn fsync(&self, id: usize) -> Result { let _handle = { let handles = handles(); @@ -165,4 +124,40 @@ impl Scheme for DebugScheme { Ok(0) } } -impl crate::scheme::KernelScheme for DebugScheme {} +impl crate::scheme::KernelScheme for DebugScheme { + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + let handle = { + let handles = handles(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + INPUT.call_once(init_input) + .receive_into_user(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "DebugScheme::read") + } + + fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + let _handle = { + let handles = handles(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + // FIXME + let mut tmp = [0_u8; 512]; + let count = buf.copy_common_bytes_to_slice(&mut tmp)?; + + Writer::new().write(&tmp[..count]); + Ok(buf.len()) + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let _handle = { + let handles = handles(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + // TODO: Copy elsewhere in the kernel? + const SRC: &[u8] = b"debug:"; + let byte_count = core::cmp::min(buf.len(), SRC.len()); + buf.limit(byte_count).expect("must succeed").copy_from_slice(&SRC[..byte_count])?; + + Ok(byte_count) + } +} diff --git a/src/scheme/event.rs b/src/scheme/event.rs index c9cf66321e..73ff9b88ac 100644 --- a/src/scheme/event.rs +++ b/src/scheme/event.rs @@ -1,10 +1,11 @@ use alloc::sync::Arc; -use core::{mem, slice}; +use core::mem; use crate::event::{EventQueue, EventQueueId, next_queue_id, queues, queues_mut}; use crate::syscall::data::Event; use crate::syscall::error::*; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; pub struct EventScheme; @@ -16,31 +17,6 @@ impl Scheme for EventScheme { Ok(id.into()) } - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let id = EventQueueId::from(id); - - let queue = { - let handles = queues(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - handle.clone() - }; - - let event_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut Event, buf.len()/mem::size_of::()) }; - Ok(queue.read(event_buf)? * mem::size_of::()) - } - - fn write(&self, id: usize, buf: &[u8]) -> Result { - let id = EventQueueId::from(id); - - let queue = { - let handles = queues(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - handle.clone() - }; - - let event_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const Event, buf.len()/mem::size_of::()) }; - Ok(queue.write(event_buf)? * mem::size_of::()) - } fn fcntl(&self, id: usize, _cmd: usize, _arg: usize) -> Result { let id = EventQueueId::from(id); @@ -49,16 +25,6 @@ impl Scheme for EventScheme { handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0)) } - fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result { - let mut i = 0; - let scheme_path = b"event:"; - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - Ok(i) - } - fn fsync(&self, id: usize) -> Result { let id = EventQueueId::from(id); @@ -71,4 +37,41 @@ impl Scheme for EventScheme { queues_mut().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0)) } } -impl crate::scheme::KernelScheme for EventScheme {} +impl crate::scheme::KernelScheme for EventScheme { + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + let id = EventQueueId::from(id); + + let queue = { + let handles = queues(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + queue.read(buf) + } + + fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + let id = EventQueueId::from(id); + + let queue = { + let handles = queues(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + handle.clone() + }; + let mut events_written = 0; + + for chunk in buf.in_exact_chunks(mem::size_of::()) { + let event = unsafe { chunk.read_exact::()? }; + if queue.write(&[event])? == 0 { + break; + } + events_written += 1; + } + + Ok(events_written * mem::size_of::()) + } + + fn kfpath(&self, _id: usize, buf: UserSliceWo) -> Result { + buf.copy_common_bytes_from_slice(b"event:") + } +} diff --git a/src/scheme/irq.rs b/src/scheme/irq.rs index c6cb16a7ff..2044e17517 100644 --- a/src/scheme/irq.rs +++ b/src/scheme/irq.rs @@ -13,9 +13,11 @@ use crate::arch::interrupt::{available_irqs_iter, bsp_apic_id, is_reserved, set_ use crate::event; use crate::interrupt::irq::acknowledge; use crate::scheme::{AtomicSchemeId, SchemeId}; +use crate::syscall::data::Stat; use crate::syscall::error::*; use crate::syscall::flag::{EventFlags, EVENT_READ, O_DIRECTORY, O_CREAT, O_STAT, MODE_CHR, MODE_DIR}; use crate::syscall::scheme::{calc_seek_offset_usize, Scheme}; +use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; pub static IRQ_SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default(); @@ -216,47 +218,6 @@ impl Scheme for IrqScheme { Ok(fd) } - fn read(&self, file: usize, buffer: &mut [u8]) -> Result { - let handles_guard = HANDLES.read(); - let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?; - - match handle { - // Ensures that the length of the buffer is larger than the size of a usize - &Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::() { - let current = COUNTS.lock()[handle_irq as usize]; - if handle_ack.load(Ordering::SeqCst) != current { - // Safe if the length of the buffer is larger than the size of a usize - assert!(buffer.len() >= mem::size_of::()); - unsafe { *(buffer.as_mut_ptr() as *mut usize) = current; } - Ok(mem::size_of::()) - } else { - Ok(0) - } - } else { - Err(Error::new(EINVAL)) - } - &Handle::Bsp => { - if buffer.len() < mem::size_of::() { - return Err(Error::new(EINVAL)); - } - if let Some(bsp_apic_id) = bsp_apic_id() { - unsafe { *(buffer.as_mut_ptr() as *mut usize) = bsp_apic_id as usize; } - Ok(mem::size_of::()) - } else { - Err(Error::new(EBADFD)) - } - } - &Handle::Avail(_, ref buf, ref offset) | &Handle::TopLevel(ref buf, ref offset) => { - let cur_offset = offset.load(Ordering::SeqCst); - let max_bytes_to_read = core::cmp::min(buf.len(), buffer.len()); - let bytes_to_read = core::cmp::max(max_bytes_to_read, cur_offset) - cur_offset; - buffer[..bytes_to_read].copy_from_slice(&buf[cur_offset..cur_offset + bytes_to_read]); - offset.fetch_add(bytes_to_read, Ordering::SeqCst); - Ok(bytes_to_read) - } - } - } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { let handles_guard = HANDLES.read(); let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?; @@ -272,67 +233,6 @@ impl Scheme for IrqScheme { } } - fn write(&self, file: usize, buffer: &[u8]) -> Result { - let handles_guard = HANDLES.read(); - let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?; - - match handle { - &Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::() { - assert!(buffer.len() >= mem::size_of::()); - - let ack = unsafe { *(buffer.as_ptr() as *const usize) }; - let current = COUNTS.lock()[handle_irq as usize]; - - if ack == current { - handle_ack.store(ack, Ordering::SeqCst); - unsafe { acknowledge(handle_irq as usize); } - Ok(mem::size_of::()) - } else { - Ok(0) - } - } else { - Err(Error::new(EINVAL)) - } - _ => Err(Error::new(EBADF)), - } - } - - fn fstat(&self, id: usize, stat: &mut syscall::data::Stat) -> Result { - let handles_guard = HANDLES.read(); - let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?; - - match *handle { - Handle::Irq { irq: handle_irq, .. } => { - stat.st_mode = MODE_CHR | 0o600; - stat.st_size = mem::size_of::() as u64; - stat.st_blocks = 1; - stat.st_blksize = mem::size_of::() as u32; - stat.st_ino = handle_irq.into(); - stat.st_nlink = 1; - } - Handle::Bsp => { - stat.st_mode = MODE_CHR | 0o400; - stat.st_size = mem::size_of::() as u64; - stat.st_blocks = 1; - stat.st_blksize = mem::size_of::() as u32; - stat.st_ino = INO_BSP; - stat.st_nlink = 1; - } - Handle::Avail(cpu_id, ref buf, _) => { - stat.st_mode = MODE_DIR | 0o700; - stat.st_size = buf.len() as u64; - stat.st_ino = INO_AVAIL | u64::from(cpu_id) << 32; - stat.st_nlink = 2; - } - Handle::TopLevel(ref buf, _) => { - stat.st_mode = MODE_DIR | 0o500; - stat.st_size = buf.len() as u64; - stat.st_ino = INO_TOPLEVEL; - stat.st_nlink = 1; - } - } - Ok(0) - } fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { Ok(0) @@ -342,24 +242,6 @@ impl Scheme for IrqScheme { Ok(EventFlags::empty()) } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let handles_guard = HANDLES.read(); - let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?; - - let scheme_path = match handle { - Handle::Irq { irq, .. } => format!("irq:{}", irq), - Handle::Bsp => format!("irq:bsp"), - Handle::Avail(cpu_id, _, _) => format!("irq:cpu-{:2x}", cpu_id), - Handle::TopLevel(_, _) => format!("irq:"), - }.into_bytes(); - - let mut i = 0; - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - Ok(i) - } fn fsync(&self, _file: usize) -> Result { Ok(0) @@ -377,4 +259,119 @@ impl Scheme for IrqScheme { Ok(0) } } -impl crate::scheme::KernelScheme for IrqScheme {} +impl crate::scheme::KernelScheme for IrqScheme { + fn kwrite(&self, file: usize, buffer: UserSliceRo) -> Result { + let handles_guard = HANDLES.read(); + let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?; + + match handle { + &Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::() { + let ack = buffer.read_usize()?; + let current = COUNTS.lock()[handle_irq as usize]; + + if ack == current { + handle_ack.store(ack, Ordering::SeqCst); + unsafe { acknowledge(handle_irq as usize); } + Ok(mem::size_of::()) + } else { + Ok(0) + } + } else { + Err(Error::new(EINVAL)) + } + _ => Err(Error::new(EBADF)), + } + } + + fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result { + let handles_guard = HANDLES.read(); + let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?; + + buf.copy_exactly(&match *handle { + Handle::Irq { irq: handle_irq, .. } => Stat { + st_mode: MODE_CHR | 0o600, + st_size: mem::size_of::() as u64, + st_blocks: 1, + st_blksize: mem::size_of::() as u32, + st_ino: handle_irq.into(), + st_nlink: 1, + ..Default::default() + }, + Handle::Bsp => Stat { + st_mode: MODE_CHR | 0o400, + st_size: mem::size_of::() as u64, + st_blocks: 1, + st_blksize: mem::size_of::() as u32, + st_ino: INO_BSP, + st_nlink: 1, + ..Default::default() + }, + Handle::Avail(cpu_id, ref buf, _) => Stat { + st_mode: MODE_DIR | 0o700, + st_size: buf.len() as u64, + st_ino: INO_AVAIL | u64::from(cpu_id) << 32, + st_nlink: 2, + ..Default::default() + }, + Handle::TopLevel(ref buf, _) => Stat { + st_mode: MODE_DIR | 0o500, + st_size: buf.len() as u64, + st_ino: INO_TOPLEVEL, + st_nlink: 1, + ..Default::default() + }, + })?; + Ok(0) + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let handles_guard = HANDLES.read(); + let handle = handles_guard.as_ref().unwrap().get(&id).ok_or(Error::new(EBADF))?; + + let scheme_path = match handle { + Handle::Irq { irq, .. } => format!("irq:{}", irq), + Handle::Bsp => format!("irq:bsp"), + Handle::Avail(cpu_id, _, _) => format!("irq:cpu-{:2x}", cpu_id), + Handle::TopLevel(_, _) => format!("irq:"), + }.into_bytes(); + + buf.copy_common_bytes_from_slice(&scheme_path) + } + fn kread(&self, file: usize, buffer: UserSliceWo) -> Result { + let handles_guard = HANDLES.read(); + let handle = handles_guard.as_ref().unwrap().get(&file).ok_or(Error::new(EBADF))?; + + match *handle { + // Ensures that the length of the buffer is larger than the size of a usize + Handle::Irq { irq: handle_irq, ack: ref handle_ack } => if buffer.len() >= mem::size_of::() { + let current = COUNTS.lock()[handle_irq as usize]; + if handle_ack.load(Ordering::SeqCst) != current { + buffer.write_usize(current)?; + Ok(mem::size_of::()) + } else { + Ok(0) + } + } else { + Err(Error::new(EINVAL)) + } + Handle::Bsp => { + if buffer.len() < mem::size_of::() { + return Err(Error::new(EINVAL)); + } + if let Some(bsp_apic_id) = bsp_apic_id() { + buffer.write_u32(bsp_apic_id)?; + Ok(mem::size_of::()) + } else { + Err(Error::new(EBADFD)) + } + } + Handle::Avail(_, ref buf, ref offset) | Handle::TopLevel(ref buf, ref offset) => { + let cur_offset = offset.load(Ordering::SeqCst); + let avail_buf = buf.get(cur_offset..).unwrap_or(&[]); + let bytes_read = buffer.copy_common_bytes_from_slice(avail_buf)?; + offset.fetch_add(bytes_read, Ordering::SeqCst); + Ok(bytes_read) + } + } + } + +} diff --git a/src/scheme/itimer.rs b/src/scheme/itimer.rs index 1c8f27d246..bccc7c6f28 100644 --- a/src/scheme/itimer.rs +++ b/src/scheme/itimer.rs @@ -1,5 +1,5 @@ use alloc::collections::BTreeMap; -use core::{mem, slice, str}; +use core::{mem, str}; use core::sync::atomic::{AtomicUsize, Ordering}; use spin::RwLock; @@ -7,6 +7,7 @@ use crate::syscall::data::ITimerSpec; use crate::syscall::error::*; use crate::syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC, EventFlags}; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; pub struct ITimerScheme { next_id: AtomicUsize, @@ -38,41 +39,6 @@ impl Scheme for ITimerScheme { Ok(id) } - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let _clock = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let time_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut ITimerSpec, buf.len()/mem::size_of::()) }; - - let mut i = 0; - while i < time_buf.len() { - time_buf[i] = ITimerSpec::default(); - i += 1; - } - - Ok(i * mem::size_of::()) - } - - fn write(&self, id: usize, buf: &[u8]) -> Result { - let _clock = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let time_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const ITimerSpec, buf.len()/mem::size_of::()) }; - - let mut i = 0; - while i < time_buf.len() { - let time = time_buf[i]; - println!("{}: {:?}", i, time); - i += 1; - } - - Ok(i * mem::size_of::()) - } - fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { Ok(0) } @@ -82,21 +48,6 @@ impl Scheme for ITimerScheme { handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(EventFlags::empty())) } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let clock = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let mut i = 0; - let scheme_path = format!("time:{}", clock).into_bytes(); - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - Ok(i) - } - fn fsync(&self, id: usize) -> Result { let handles = self.handles.read(); handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0)) @@ -106,4 +57,48 @@ impl Scheme for ITimerScheme { self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0)) } } -impl crate::scheme::KernelScheme for ITimerScheme {} +impl crate::scheme::KernelScheme for ITimerScheme { + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + let _clock = { + let handles = self.handles.read(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + let mut specs_read = 0; + + for current_chunk in buf.in_exact_chunks(mem::size_of::()) { + current_chunk.copy_exactly(&ITimerSpec::default())?; + + specs_read += 1; + } + + Ok(specs_read * mem::size_of::()) + } + + fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + let _clock = { + let handles = self.handles.read(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + let mut specs_written = 0; + + for chunk in buf.in_exact_chunks(mem::size_of::()) { + let time = unsafe { chunk.read_exact::()? }; + + println!("{}: {:?}", specs_written, time); + specs_written += 1; + } + + Ok(specs_written * mem::size_of::()) + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let clock = { + let handles = self.handles.read(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + buf.copy_common_bytes_from_slice(format!("time:{}", clock).as_bytes()) + } + +} diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs index f8b0fa7c01..7586000b80 100644 --- a/src/scheme/memory.rs +++ b/src/scheme/memory.rs @@ -8,6 +8,7 @@ use crate::memory::{free_frames, used_frames, PAGE_SIZE}; use crate::syscall::data::{Map, StatVfs}; use crate::syscall::error::*; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::UserSliceWo; pub struct MemoryScheme; @@ -17,7 +18,7 @@ impl MemoryScheme { } pub fn fmap_anonymous(addr_space: &Arc>, map: &Map) -> Result { - let (requested_page, page_count) = crate::syscall::validate::validate_region(map.address, map.size)?; + let (requested_page, page_count) = crate::syscall::usercopy::validate_region(map.address, map.size)?; let page = addr_space .write() @@ -33,18 +34,6 @@ impl Scheme for MemoryScheme { Ok(0) } - fn fstatvfs(&self, _file: usize, stat: &mut StatVfs) -> Result { - let used = used_frames() as u64; - let free = free_frames() as u64; - - stat.f_bsize = PAGE_SIZE as u32; - stat.f_blocks = used + free; - stat.f_bfree = free; - stat.f_bavail = stat.f_bfree; - - Ok(0) - } - fn fmap(&self, _id: usize, map: &Map) -> Result { Self::fmap_anonymous(&Arc::clone(context::current()?.read().addr_space()?), map) } @@ -53,16 +42,6 @@ impl Scheme for MemoryScheme { Ok(0) } - fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result { - let mut i = 0; - let scheme_path = b"memory:"; - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - Ok(i) - } - fn close(&self, _id: usize) -> Result { Ok(0) } @@ -71,4 +50,26 @@ impl crate::scheme::KernelScheme for MemoryScheme { fn kfmap(&self, _number: usize, addr_space: &Arc>, map: &Map, _consume: bool) -> Result { Self::fmap_anonymous(addr_space, map) } + fn kfpath(&self, _id: usize, dst: UserSliceWo) -> Result { + // TODO: Copy scheme name elsewhere in the kernel? + const SRC: &[u8] = b"memory:"; + let byte_count = core::cmp::min(SRC.len(), dst.len()); + dst.limit(byte_count).ok_or(Error::new(EINVAL))?.copy_from_slice(SRC)?; + Ok(0) + } + fn kfstatvfs(&self, _file: usize, dst: UserSliceWo) -> Result { + let used = used_frames() as u64; + let free = free_frames() as u64; + + let stat = StatVfs { + f_bsize: PAGE_SIZE.try_into().map_err(|_| Error::new(EOVERFLOW))?, + f_blocks: used + free, + f_bfree: free, + f_bavail: free, + }; + dst.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_from_slice(&stat)?; + + Ok(0) + } + } diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 86c1d80c8d..e8b4ed1e30 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -21,6 +21,7 @@ use crate::context::file::FileDescription; use crate::context::{memory::AddrSpace, file::FileDescriptor}; use crate::syscall::error::*; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::{UserSliceRo, UserSliceWo}; #[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))] use self::acpi::AcpiScheme; @@ -176,15 +177,13 @@ impl SchemeList { } } - pub fn make_ns(&mut self, from: SchemeNamespace, names: &[&str]) -> Result { + pub fn make_ns(&mut self, from: SchemeNamespace, names: impl IntoIterator>) -> Result { // Create an empty namespace let to = self.new_ns(); // Copy requested scheme IDs - for name in names.iter() { - let id = if let Some((id, _scheme)) = self.get_name(from, name) { - id - } else { + for name in names { + let Some((id, _scheme)) = self.get_name(from, &name) else { return Err(Error::new(ENODEV)); }; @@ -315,11 +314,30 @@ pub trait KernelScheme: Scheme + Send + Sync + 'static { fn kopen(&self, path: &str, flags: usize, caller: CallerCtx) -> Result { self.open(path, flags, caller.uid, caller.gid).map(OpenResult::SchemeLocal) } - fn kdup(&self, old_id: usize, buf: &[u8], _caller: CallerCtx) -> Result { - self.dup(old_id, buf).map(OpenResult::SchemeLocal) + fn kdup(&self, old_id: usize, buf: UserSliceRo, _caller: CallerCtx) -> Result { + Err(Error::new(EOPNOTSUPP)) + } + fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + Err(Error::new(EBADF)) + } + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + Err(Error::new(EBADF)) + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + Err(Error::new(EBADF)) + } + fn kfutimens(&self, id: usize, buf: UserSliceRo) -> Result { + Err(Error::new(EBADF)) + } + fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result { + Err(Error::new(EBADF)) + } + fn kfstatvfs(&self, id: usize, buf: UserSliceWo) -> Result { + Err(Error::new(EBADF)) } } +#[derive(Debug)] pub enum OpenResult { SchemeLocal(usize), External(Arc>), diff --git a/src/scheme/pipe.rs b/src/scheme/pipe.rs index 3543af3242..edfd6ed565 100644 --- a/src/scheme/pipe.rs +++ b/src/scheme/pipe.rs @@ -10,10 +10,11 @@ use crate::scheme::SchemeId; use crate::sync::WaitCondition; use crate::syscall::error::{Error, Result, EAGAIN, EBADF, EINTR, EINVAL, ENOENT, EPIPE, ESPIPE}; use crate::syscall::flag::{EventFlags, EVENT_READ, EVENT_WRITE, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK, MODE_FIFO}; -use crate::syscall::scheme::Scheme; +use crate::syscall::scheme::{CallerCtx, Scheme}; use crate::syscall::data::Stat; +use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; -use super::KernelScheme; +use super::{KernelScheme, OpenResult}; // TODO: Preallocate a number of scheme IDs, since there can only be *one* root namespace, and // therefore only *one* pipe scheme. @@ -65,109 +66,6 @@ impl PipeScheme { } impl Scheme for PipeScheme { - fn open(&self, path: &str, flags: usize, _uid: u32, _gid: u32) -> Result { - if !path.trim_start_matches('/').is_empty() { - return Err(Error::new(ENOENT)); - } - - let (read_id, _) = pipe(flags)?; - - Ok(read_id) - } - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let (is_write_not_read, key) = from_raw_id(id); - - if is_write_not_read { - return Err(Error::new(EBADF)); - } - let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); - - loop { - let mut vec = pipe.queue.lock(); - - let (s1, s2) = vec.as_slices(); - let s1_count = core::cmp::min(buf.len(), s1.len()); - - let (s1_dst, s2_buf) = buf.split_at_mut(s1_count); - s1_dst.copy_from_slice(&s1[..s1_count]); - - let s2_count = core::cmp::min(s2_buf.len(), s2.len()); - s2_buf[..s2_count].copy_from_slice(&s2[..s2_count]); - - let bytes_read = s1_count + s2_count; - let _ = vec.drain(..bytes_read); - - if bytes_read > 0 { - event::trigger(pipe_scheme_id(), key | WRITE_NOT_READ_BIT, EVENT_WRITE); - pipe.write_condition.notify(); - - return Ok(bytes_read); - } else if buf.is_empty() { - return Ok(0); - } - - if !pipe.writer_is_alive.load(Ordering::SeqCst) { - return Ok(0); - } else if pipe.read_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK { - return Err(Error::new(EAGAIN)); - } else if !pipe.read_condition.wait(vec, "PipeRead::read") { - return Err(Error::new(EINTR)); - } - } - } - fn write(&self, id: usize, buf: &[u8]) -> Result { - let (is_write_not_read, key) = from_raw_id(id); - - if !is_write_not_read { - return Err(Error::new(EBADF)); - } - let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); - - loop { - let mut vec = pipe.queue.lock(); - - let bytes_left = MAX_QUEUE_SIZE.saturating_sub(vec.len()); - let byte_count = core::cmp::min(bytes_left, buf.len()); - - vec.extend(buf[..byte_count].iter()); - - if byte_count > 0 { - event::trigger(pipe_scheme_id(), key, EVENT_READ); - pipe.read_condition.notify(); - - return Ok(byte_count); - } else if buf.is_empty() { - return Ok(0); - } - - if !pipe.reader_is_alive.load(Ordering::SeqCst) { - return Err(Error::new(EPIPE)); - } else if pipe.write_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK { - return Err(Error::new(EAGAIN)); - } else if !pipe.write_condition.wait(vec, "PipeWrite::write") { - return Err(Error::new(EINTR)); - } - } - } - - fn dup(&self, old_id: usize, buf: &[u8]) -> Result { - let (is_writer_not_reader, key) = from_raw_id(old_id); - - if is_writer_not_reader { - return Err(Error::new(EBADF)); - } - if buf != b"write" { - return Err(Error::new(EINVAL)); - } - - let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); - - if pipe.has_run_dup.swap(true, Ordering::SeqCst) { - return Err(Error::new(EBADF)); - } - - Ok(key | WRITE_NOT_READ_BIT) - } fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { let (is_writer_not_reader, key) = from_raw_id(id); @@ -208,22 +106,6 @@ impl Scheme for PipeScheme { Err(Error::new(EBADF)) } - fn fpath(&self, _id: usize, buf: &mut [u8]) -> Result { - let scheme_path = b"pipe:"; - let to_copy = core::cmp::min(buf.len(), scheme_path.len()); - buf[..to_copy].copy_from_slice(&scheme_path[..to_copy]); - Ok(to_copy) - } - - fn fstat(&self, _id: usize, stat: &mut Stat) -> Result { - *stat = Stat { - st_mode: MODE_FIFO | 0o666, - ..Default::default() - }; - - Ok(0) - } - fn fsync(&self, _id: usize) -> Result { Ok(0) } @@ -273,4 +155,134 @@ pub struct Pipe { has_run_dup: AtomicBool, } -impl crate::scheme::KernelScheme for PipeScheme {} +impl KernelScheme for PipeScheme { + fn kdup(&self, old_id: usize, user_buf: UserSliceRo, _ctx: CallerCtx) -> Result { + let (is_writer_not_reader, key) = from_raw_id(old_id); + + if is_writer_not_reader { + return Err(Error::new(EBADF)); + } + + let mut buf = [0_u8; 5]; + + if user_buf.copy_common_bytes_to_slice(&mut buf)? < 5 || buf != *b"write" { + return Err(Error::new(EINVAL)); + } + + let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); + + if pipe.has_run_dup.swap(true, Ordering::SeqCst) { + return Err(Error::new(EBADF)); + } + + Ok(OpenResult::SchemeLocal(key | WRITE_NOT_READ_BIT)) + } + fn kopen(&self, path: &str, flags: usize, _ctx: CallerCtx) -> Result { + if !path.trim_start_matches('/').is_empty() { + return Err(Error::new(ENOENT)); + } + + let (read_id, _) = pipe(flags)?; + + Ok(OpenResult::SchemeLocal(read_id)) + } + + fn kread(&self, id: usize, user_buf: UserSliceWo) -> Result { + let (is_write_not_read, key) = from_raw_id(id); + + if is_write_not_read { + return Err(Error::new(EBADF)); + } + let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); + + loop { + let mut vec = pipe.queue.lock(); + + let (s1, s2) = vec.as_slices(); + let s1_count = core::cmp::min(user_buf.len(), s1.len()); + + let (s1_dst, s2_buf) = user_buf.split_at(s1_count).expect("s1_count <= user_buf.len()"); + s1_dst.copy_from_slice(&s1[..s1_count])?; + + let s2_count = core::cmp::min(s2_buf.len(), s2.len()); + s2_buf.limit(s2_count).expect("s2_count <= s2_buf.len()").copy_from_slice(&s2[..s2_count])?; + + let bytes_read = s1_count + s2_count; + let _ = vec.drain(..bytes_read); + + if bytes_read > 0 { + event::trigger(pipe_scheme_id(), key | WRITE_NOT_READ_BIT, EVENT_WRITE); + pipe.write_condition.notify(); + + return Ok(bytes_read); + } else if user_buf.is_empty() { + return Ok(0); + } + + if !pipe.writer_is_alive.load(Ordering::SeqCst) { + return Ok(0); + } else if pipe.read_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK { + return Err(Error::new(EAGAIN)); + } else if !pipe.read_condition.wait(vec, "PipeRead::read") { + return Err(Error::new(EINTR)); + } + } + } + fn kwrite(&self, id: usize, user_buf: UserSliceRo) -> Result { + let (is_write_not_read, key) = from_raw_id(id); + + if !is_write_not_read { + return Err(Error::new(EBADF)); + } + let pipe = Arc::clone(PIPES.read().get(&key).ok_or(Error::new(EBADF))?); + + loop { + let mut vec = pipe.queue.lock(); + + let bytes_left = MAX_QUEUE_SIZE.saturating_sub(vec.len()); + let bytes_to_write = core::cmp::min(bytes_left, user_buf.len()); + let src_buf = user_buf.limit(bytes_to_write).expect("bytes_to_write <= user_buf.len()"); + + const TMPBUF_SIZE: usize = 512; + let mut tmp_buf = [0_u8; TMPBUF_SIZE]; + + let mut bytes_written = 0; + + // TODO: Modify VecDeque so that the unwritten portions can be accessed directly? + for (idx, chunk) in src_buf.in_variable_chunks(TMPBUF_SIZE).enumerate() { + let chunk_byte_count = match chunk.copy_common_bytes_to_slice(&mut tmp_buf) { + Ok(c) => c, + Err(_) if idx > 0 => break, + Err(error) => return Err(error), + }; + vec.extend(&tmp_buf[..chunk_byte_count]); + bytes_written += chunk_byte_count; + } + + if bytes_written > 0 { + event::trigger(pipe_scheme_id(), key, EVENT_READ); + pipe.read_condition.notify(); + + return Ok(bytes_written); + } else if user_buf.is_empty() { + return Ok(0); + } + + if !pipe.reader_is_alive.load(Ordering::SeqCst) { + return Err(Error::new(EPIPE)); + } else if pipe.write_flags.load(Ordering::SeqCst) & O_NONBLOCK == O_NONBLOCK { + return Err(Error::new(EAGAIN)); + } else if !pipe.write_condition.wait(vec, "PipeWrite::write") { + return Err(Error::new(EINTR)); + } + } + } + fn kfstat(&self, _id: usize, buf: UserSliceWo) -> Result { + buf.copy_exactly(&Stat { + st_mode: MODE_FIFO | 0o666, + ..Default::default() + })?; + + Ok(0) + } +} diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index d4bbc6acfe..3384e37d9c 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -1,6 +1,6 @@ use crate::{ arch::paging::{mapper::InactiveFlusher, Page, RmmA, RmmArch, VirtualAddress}, - context::{self, Context, ContextId, Status, file::{FileDescription, FileDescriptor}, memory::{AddrSpace, Grant, new_addrspace, map_flags, Region}}, + context::{self, Context, ContextId, Status, file::{FileDescription, FileDescriptor}, memory::{AddrSpace, Grant, new_addrspace, map_flags, Region}, BorrowedHtBuf}, memory::PAGE_SIZE, ptrace, scheme::{self, FileHandle, KernelScheme, SchemeId}, @@ -12,7 +12,7 @@ use crate::{ error::*, flag::*, scheme::{calc_seek_offset_usize, Scheme}, - self, + self, usercopy::{UserSliceWo, UserSliceRo}, }, }; @@ -23,9 +23,8 @@ use alloc::{ sync::Arc, vec::Vec, }; +use ::syscall::CallerCtx; use core::{ - cmp, - convert::TryFrom, mem, slice, str, @@ -33,12 +32,13 @@ use core::{ }; use spin::{Once, RwLock}; -fn read_from(dst: &mut [u8], src: &[u8], offset: &mut usize) -> Result { - let byte_count = cmp::min(dst.len(), src.len().saturating_sub(*offset)); - let next_offset = offset.saturating_add(byte_count); - dst[..byte_count].copy_from_slice(&src[*offset..next_offset]); - *offset = next_offset; - Ok(byte_count) +use super::OpenResult; + +fn read_from(dst: UserSliceWo, src: &[u8], offset: &mut usize) -> Result { + let avail_src = src.get(*offset..).unwrap_or(&[]); + let bytes_copied = dst.copy_common_bytes_from_slice(avail_src)?; + *offset = offset.checked_add(bytes_copied).ok_or(Error::new(EOVERFLOW))?; + Ok(bytes_copied) } fn with_context(pid: ContextId, callback: F) -> Result @@ -584,74 +584,6 @@ impl Scheme for ProcScheme { self.open_inner(pid, parts.next(), flags, uid, gid) } - /// Dup is currently used to implement clone() and execve(). - fn dup(&self, old_id: usize, buf: &[u8]) -> Result { - let info = { - let handles = self.handles.read(); - let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?; - - handle.info.clone() - }; - - let handle = |operation, data| Handle { - info: Info { - flags: 0, - pid: info.pid, - operation, - }, - data, - }; - - self.new_handle(match info.operation { - Operation::OpenViaDup => { - let (uid, gid) = match &*context::contexts().current().ok_or(Error::new(ESRCH))?.read() { - context => (context.euid, context.egid), - }; - return self.open_inner(info.pid, Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?).filter(|s| !s.is_empty()), O_RDWR | O_CLOEXEC, uid, gid); - }, - - Operation::Filetable { ref filetable } => { - // TODO: Maybe allow userspace to either copy or transfer recently dupped file - // descriptors between file tables. - if buf != b"copy" { - return Err(Error::new(EINVAL)); - } - let new_filetable = Arc::try_new(RwLock::new(filetable.read().clone())).map_err(|_| Error::new(ENOMEM))?; - - handle(Operation::Filetable { filetable: new_filetable }, OperationData::Other) - } - Operation::AddrSpace { ref addrspace } => { - let (operation, is_mem) = match buf { - // TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But - // in that case, what scheme? - b"empty" => (Operation::AddrSpace { addrspace: new_addrspace()? }, false), - b"exclusive" => (Operation::AddrSpace { addrspace: addrspace.write().try_clone()? }, false), - b"mem" => (Operation::Memory { addrspace: Arc::clone(addrspace) }, true), - b"mmap-min-addr" => (Operation::MmapMinAddr(Arc::clone(addrspace)), false), - - grant_handle if grant_handle.starts_with(b"grant-") => { - let start_addr = usize::from_str_radix(core::str::from_utf8(&grant_handle[6..]).map_err(|_| Error::new(EINVAL))?, 16).map_err(|_| Error::new(EINVAL))?; - (Operation::GrantHandle { - description: Arc::clone(&addrspace.read().grants.contains(VirtualAddress::new(start_addr)).ok_or(Error::new(EINVAL))?.desc_opt.as_ref().ok_or(Error::new(EINVAL))?.desc.description) - }, false) - } - - _ => return Err(Error::new(EINVAL)), - }; - - handle(operation, if is_mem { OperationData::Memory(MemData { offset: VirtualAddress::new(0) }) } else { OperationData::Offset(0) }) - } - Operation::Sigactions(ref sigactions) => { - let new = match buf { - b"empty" => Context::empty_actions(), - b"copy" => Arc::new(RwLock::new(sigactions.read().clone())), - _ => return Err(Error::new(EINVAL)), - }; - handle(Operation::Sigactions(new), OperationData::Other) - } - _ => return Err(Error::new(EINVAL)), - }) - } fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { let mut handles = self.handles.write(); @@ -663,443 +595,6 @@ impl Scheme for ProcScheme { Ok(value) } - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - // Don't hold a global lock during the context switch later on - let info = { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - handle.info.clone() - }; - - match info.operation { - Operation::Static(_) => { - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.static_data().expect("operations can't change"); - - let len = cmp::min(data.buf.len() - data.offset, buf.len()); - buf[..len].copy_from_slice(&data.buf[data.offset .. data.offset + len]); - data.offset += len; - Ok(len) - }, - Operation::Memory { addrspace } => { - // Won't context switch, don't worry about the locks - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.mem_data().expect("operations can't change"); - - let mut bytes_read = 0; - - for chunk_opt in ptrace::context_memory(&mut *addrspace.write(), data.offset, buf.len()) { - let (chunk, _writable) = chunk_opt.ok_or(Error::new(EFAULT))?; - let dst_slice = &mut buf[bytes_read..bytes_read + chunk.len()]; - unsafe { - chunk.as_mut_ptr().copy_to_nonoverlapping(dst_slice.as_mut_ptr(), dst_slice.len()); - } - bytes_read += chunk.len(); - } - - data.offset = VirtualAddress::new(data.offset.data() + bytes_read); - Ok(bytes_read) - }, - // TODO: Support reading only a specific address range. Maybe using seek? - Operation::AddrSpace { addrspace } => { - let mut handles = self.handles.write(); - let offset = if let OperationData::Offset(ref mut offset) = handles.get_mut(&id).ok_or(Error::new(EBADF))?.data { - offset - } else { - return Err(Error::new(EBADFD)); - }; - - // TODO: Define a struct somewhere? - const RECORD_SIZE: usize = mem::size_of::() * 4; - let records = buf.array_chunks_mut::(); - - let addrspace = addrspace.read(); - let mut bytes_read = 0; - - for (record_bytes, grant) in records.zip(addrspace.grants.iter()).skip(*offset / RECORD_SIZE) { - let mut qwords = record_bytes.array_chunks_mut::<{mem::size_of::()}>(); - qwords.next().unwrap().copy_from_slice(&usize::to_ne_bytes(grant.start_address().data())); - qwords.next().unwrap().copy_from_slice(&usize::to_ne_bytes(grant.size())); - qwords.next().unwrap().copy_from_slice(&usize::to_ne_bytes(map_flags(grant.flags()).bits() | if grant.desc_opt.is_some() { 0x8000_0000 } else { 0 })); - qwords.next().unwrap().copy_from_slice(&usize::to_ne_bytes(grant.desc_opt.as_ref().map_or(0, |d| d.offset))); - bytes_read += RECORD_SIZE; - } - - *offset += bytes_read; - Ok(bytes_read) - } - - Operation::Regs(kind) => { - union Output { - float: FloatRegisters, - int: IntRegisters, - env: EnvRegisters, - } - - let (output, size) = match kind { - RegsKind::Float => with_context(info.pid, |context| { - // NOTE: The kernel will never touch floats - - Ok((Output { float: context.get_fx_regs() }, mem::size_of::())) - })?, - RegsKind::Int => try_stop_context(info.pid, |context| match unsafe { ptrace::regs_for(context) } { - None => { - assert!(!context.running, "try_stop_context is broken, clearly"); - println!("{}:{}: Couldn't read registers from stopped process", file!(), line!()); - Err(Error::new(ENOTRECOVERABLE)) - }, - Some(stack) => { - let mut regs = IntRegisters::default(); - stack.save(&mut regs); - Ok((Output { int: regs }, mem::size_of::())) - } - })?, - RegsKind::Env => { - ( - Output { env: self.read_env_regs(&info)? }, - mem::size_of::() - ) - } - }; - - let bytes = unsafe { - slice::from_raw_parts(&output as *const _ as *const u8, mem::size_of::()) - }; - let len = cmp::min(buf.len(), size); - buf[..len].copy_from_slice(&bytes[..len]); - - Ok(len) - }, - Operation::Trace => { - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.trace_data().expect("operations can't change"); - - // Wait for event - if handle.info.flags & O_NONBLOCK != O_NONBLOCK { - ptrace::wait(handle.info.pid)?; - } - - // Check if context exists - with_context(handle.info.pid, |_| Ok(()))?; - - // Read events - let slice = unsafe { - slice::from_raw_parts_mut( - buf.as_mut_ptr() as *mut PtraceEvent, - buf.len() / mem::size_of::() - ) - }; - let (read, reached) = ptrace::Session::with_session(info.pid, |session| { - let mut data = session.data.lock(); - Ok((data.recv_events(slice), data.is_reached())) - })?; - - // Save child processes in a list of processes to restart - for event in &slice[..read] { - if event.cause == PTRACE_EVENT_CLONE { - data.clones.push(ContextId::from(event.a)); - } - } - - // If there are no events, and breakpoint isn't reached, we - // must not have waited. - if read == 0 && !reached { - assert!(handle.info.flags & O_NONBLOCK == O_NONBLOCK, "wait woke up spuriously??"); - return Err(Error::new(EAGAIN)); - } - - // Return read events - Ok(read * mem::size_of::()) - } - Operation::Name => read_from(buf, context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().name.as_bytes(), &mut 0), - Operation::Sigstack => read_from(buf, &context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().sigstack.unwrap_or(!0).to_ne_bytes(), &mut 0), - Operation::Attr(attr) => { - let src_buf = match (attr, &*Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?).read()) { - (Attr::Uid, context) => context.euid.to_string(), - (Attr::Gid, context) => context.egid.to_string(), - }.into_bytes(); - - read_from(buf, &src_buf, &mut 0) - } - Operation::Filetable { .. } => { - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.static_data().expect("operations can't change"); - - read_from(buf, &data.buf, &mut data.offset) - } - Operation::MmapMinAddr(ref addrspace) => { - let val = addrspace.read().mmap_min; - *buf.array_chunks_mut::<{mem::size_of::()}>().next().unwrap() = usize::to_ne_bytes(val); - Ok(mem::size_of::()) - } - Operation::SchedAffinity => { - // TODO: Deduplicate code - let val = context::contexts().get(info.pid).ok_or(Error::new(EBADFD))?.read().sched_affinity.map_or(usize::MAX, |a| a % crate::cpu_count()); - *buf.array_chunks_mut::<{mem::size_of::()}>().next().unwrap() = usize::to_ne_bytes(val); - Ok(mem::size_of::()) - } - // TODO: Replace write() with SYS_DUP_FORWARD. - // TODO: Find a better way to switch address spaces, since they also require switching - // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it - // with `/ctx` - _ => Err(Error::new(EBADF)), - } - } - - fn write(&self, id: usize, buf: &[u8]) -> Result { - // Don't hold a global lock during the context switch later on - let info = { - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - handle.continue_ignored_children(); - handle.info.clone() - }; - - match info.operation { - Operation::Static(_) => Err(Error::new(EBADF)), - Operation::Memory { addrspace } => { - // Won't context switch, don't worry about the locks - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - let data = handle.data.mem_data().expect("operations can't change"); - - let mut bytes_written = 0; - - for chunk_opt in ptrace::context_memory(&mut *addrspace.write(), data.offset, buf.len()) { - let (chunk, writable) = chunk_opt.ok_or(Error::new(EFAULT))?; - - if !writable { return Err(Error::new(EACCES)); } - - let src_slice = &buf[bytes_written..bytes_written + chunk.len()]; - unsafe { - chunk.as_mut_ptr().copy_from_nonoverlapping(src_slice.as_ptr(), src_slice.len()); - } - bytes_written += chunk.len(); - } - - data.offset = data.offset.add(bytes_written); - Ok(bytes_written) - }, - Operation::AddrSpace { addrspace } => { - let mut chunks = buf.array_chunks::<{mem::size_of::()}>().copied().map(usize::from_ne_bytes); - let mut words_read = 0; - let mut next = || { - words_read += 1; - chunks.next().ok_or(Error::new(EINVAL)) - }; - - match next()? { - op @ ADDRSPACE_OP_MMAP | op @ ADDRSPACE_OP_TRANSFER => { - let fd = next()?; - let offset = next()?; - let (page, page_count) = crate::syscall::validate_region(next()?, next()?)?; - let flags = MapFlags::from_bits(next()?).ok_or(Error::new(EINVAL))?; - - if !flags.contains(MapFlags::MAP_FIXED) { - return Err(Error::new(EOPNOTSUPP)); - } - - let (scheme, number) = extract_scheme_number(fd)?; - - scheme.kfmap(number, &addrspace, &Map { offset, size: page_count * PAGE_SIZE, address: page.start_address().data(), flags }, op == ADDRSPACE_OP_TRANSFER)?; - } - ADDRSPACE_OP_MUNMAP => { - let (page, page_count) = crate::syscall::validate_region(next()?, next()?)?; - - addrspace.write().munmap(page, page_count); - } - ADDRSPACE_OP_MPROTECT => { - let (page, page_count) = crate::syscall::validate_region(next()?, next()?)?; - let flags = MapFlags::from_bits(next()?).ok_or(Error::new(EINVAL))?; - - addrspace.write().mprotect(page, page_count, flags)?; - } - _ => return Err(Error::new(EINVAL)), - } - Ok(words_read * mem::size_of::()) - } - Operation::Regs(kind) => match kind { - RegsKind::Float => { - if buf.len() < mem::size_of::() { - return Ok(0); - } - if (buf.as_ptr() as usize) % mem::align_of::() != 0 { - return Err(Error::new(EINVAL)); - } - let regs = unsafe { - *(buf as *const _ as *const FloatRegisters) - }; - - with_context_mut(info.pid, |context| { - // NOTE: The kernel will never touch floats - - // Ignore the rare case of floating point - // registers being uninitiated - let _ = context.set_fx_regs(regs); - - Ok(mem::size_of::()) - }) - }, - RegsKind::Int => { - if buf.len() < mem::size_of::() { - return Ok(0); - } - if (buf.as_ptr() as usize) % mem::align_of::() != 0 { - return Err(Error::new(EINVAL)); - } - let regs = unsafe { - *(buf as *const _ as *const IntRegisters) - }; - - try_stop_context(info.pid, |context| match unsafe { ptrace::regs_for_mut(context) } { - None => { - println!("{}:{}: Couldn't read registers from stopped process", file!(), line!()); - Err(Error::new(ENOTRECOVERABLE)) - }, - Some(stack) => { - stack.load(®s); - - Ok(mem::size_of::()) - } - }) - } - RegsKind::Env => { - if buf.len() < mem::size_of::() { - return Ok(0); - } - if (buf.as_ptr() as usize) % mem::align_of::() != 0 { - return Err(Error::new(EINVAL)); - } - let regs = unsafe { - *(buf as *const _ as *const EnvRegisters) - }; - self.write_env_regs(&info, regs)?; - Ok(mem::size_of::()) - } - }, - Operation::Trace => { - if buf.len() < mem::size_of::() { - return Ok(0); - } - - let mut bytes = [0; mem::size_of::()]; - let len = bytes.len(); - bytes.copy_from_slice(&buf[0..len]); - let op = u64::from_ne_bytes(bytes); - let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?; - - // Set next breakpoint - ptrace::Session::with_session(info.pid, |session| { - session.data.lock().set_breakpoint( - Some(op) - .filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK)) - ); - Ok(()) - })?; - - if op.contains(PTRACE_STOP_SINGLESTEP) { - try_stop_context(info.pid, |context| { - match unsafe { ptrace::regs_for_mut(context) } { - None => { - println!("{}:{}: Couldn't read registers from stopped process", file!(), line!()); - Err(Error::new(ENOTRECOVERABLE)) - }, - Some(stack) => { - stack.set_singlestep(true); - Ok(()) - } - } - })?; - } - - // disable the ptrace_stop flag, which is used in some cases - with_context_mut(info.pid, |context| { - context.ptrace_stop = false; - Ok(()) - })?; - - // and notify the tracee's WaitCondition, which is used in other cases - ptrace::Session::with_session(info.pid, |session| { - session.tracee.notify(); - Ok(()) - })?; - - Ok(mem::size_of::()) - }, - Operation::Name => { - let utf8 = alloc::string::String::from_utf8(buf.to_vec()).map_err(|_| Error::new(EINVAL))?; - context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.write().name = utf8.into(); - Ok(buf.len()) - } - Operation::Sigstack => { - let bytes = <[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?; - let sigstack = usize::from_ne_bytes(bytes); - context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.write().sigstack = (sigstack != !0).then(|| sigstack); - Ok(buf.len()) - } - Operation::Attr(attr) => { - let context_lock = Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?); - let id = core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?.parse::().map_err(|_| Error::new(EINVAL))?; - - match attr { - Attr::Uid => context_lock.write().euid = id, - Attr::Gid => context_lock.write().egid = id, - } - Ok(buf.len()) - } - Operation::Filetable { .. } => Err(Error::new(EBADF)), - - Operation::CurrentFiletable => { - let filetable_fd = usize::from_ne_bytes(<[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?); - let (hopefully_this_scheme, number) = extract_scheme_number(filetable_fd)?; - - let filetable = hopefully_this_scheme.as_filetable(number)?; - - self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.info.operation = Operation::AwaitingFiletableChange(filetable); - - Ok(mem::size_of::()) - } - Operation::CurrentAddrSpace { .. } => { - let mut iter = buf.array_chunks::<{mem::size_of::()}>().copied().map(usize::from_ne_bytes); - let addrspace_fd = iter.next().ok_or(Error::new(EINVAL))?; - let sp = iter.next().ok_or(Error::new(EINVAL))?; - let ip = iter.next().ok_or(Error::new(EINVAL))?; - - let (hopefully_this_scheme, number) = extract_scheme_number(addrspace_fd)?; - let space = hopefully_this_scheme.as_addrspace(number)?; - - self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.info.operation = Operation::AwaitingAddrSpaceChange { new: space, new_sp: sp, new_ip: ip }; - - Ok(3 * mem::size_of::()) - } - Operation::CurrentSigactions => { - let sigactions_fd = usize::from_ne_bytes(<[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?); - let (hopefully_this_scheme, number) = extract_scheme_number(sigactions_fd)?; - let sigactions = hopefully_this_scheme.as_sigactions(number)?; - self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.info.operation = Operation::AwaitingSigactionsChange(sigactions); - Ok(mem::size_of::()) - } - Operation::MmapMinAddr(ref addrspace) => { - let val = usize::from_ne_bytes(<[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?); - if val % PAGE_SIZE != 0 || val > crate::USER_END_OFFSET { return Err(Error::new(EINVAL)); } - addrspace.write().mmap_min = val; - Ok(mem::size_of::()) - } - // TODO: Deduplicate code. - Operation::SchedAffinity => { - let val = usize::from_ne_bytes(<[u8; mem::size_of::()]>::try_from(buf).map_err(|_| Error::new(EINVAL))?); - context::contexts().get(info.pid).ok_or(Error::new(EBADFD))?.write().sched_affinity = if val == usize::MAX { None } else { Some(val % crate::cpu_count()) }; - Ok(mem::size_of::()) - } - - _ => Err(Error::new(EBADF)), - } - } - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { let mut handles = self.handles.write(); let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -1123,57 +618,6 @@ impl Scheme for ProcScheme { } } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - - let path = format!("proc:{}/{}", handle.info.pid.into(), match handle.info.operation { - Operation::Memory { .. } => "mem", - Operation::Regs(RegsKind::Float) => "regs/float", - Operation::Regs(RegsKind::Int) => "regs/int", - Operation::Regs(RegsKind::Env) => "regs/env", - Operation::Trace => "trace", - Operation::Static(path) => path, - Operation::Name => "name", - Operation::Sigstack => "sigstack", - Operation::Attr(Attr::Uid) => "uid", - Operation::Attr(Attr::Gid) => "gid", - Operation::Filetable { .. } => "filetable", - Operation::AddrSpace { .. } => "addrspace", - Operation::Sigactions(_) => "sigactions", - Operation::CurrentAddrSpace => "current-addrspace", - Operation::CurrentFiletable => "current-filetable", - Operation::CurrentSigactions => "current-sigactions", - Operation::OpenViaDup => "open-via-dup", - Operation::MmapMinAddr(_) => "mmap-min-addr", - Operation::SchedAffinity => "sched-affinity", - - _ => return Err(Error::new(EOPNOTSUPP)), - }); - - read_from(buf, path.as_bytes(), &mut 0) - } - - fn fstat(&self, id: usize, stat: &mut Stat) -> Result { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - - stat.st_size = match handle.data { - OperationData::Static(ref data) => (data.buf.len() - data.offset) as u64, - _ => 0, - }; - *stat = Stat { - st_mode: MODE_FILE | 0o666, - st_size: match handle.data { - OperationData::Static(ref data) => (data.buf.len() - data.offset) as u64, - _ => 0, - }, - - ..Stat::default() - }; - - Ok(0) - } fn close(&self, id: usize) -> Result { let mut handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?; @@ -1274,31 +718,20 @@ impl KernelScheme for ProcScheme { match info.operation { Operation::GrantHandle { ref description } => { - let current_space = AddrSpace::current()?; + // The map struct will probably reside in kernel memory, on the stack, and for that + // it would be very insecure not to use the pinned head/tail buffer. + let mut buf = BorrowedHtBuf::head()?; + // TODO: This can be safe + let map_dst = unsafe { buf.use_for_struct()? }; + *map_dst = *map; - // Copy Map to user memory - let page_count = 1; // TODO: find size required to store Map - let page = current_space - .write() - .mmap(None, page_count, MapFlags::PROT_READ, |page, flags, mapper, flusher| { - Ok(Grant::zeroed(page, page_count, flags, mapper, flusher)?) - })?; - - // Write Map using kernel's physmap - let (phys, _flags) = current_space.read().table.utable.translate(page.start_address()).expect("could not find mapping that was just made"); - unsafe { core::ptr::write(RmmA::phys_to_virt(phys).data() as *mut Map, *map); } - - // Scheme fmap with Map in user memory let (scheme_id, number) = { let description = description.read(); (description.scheme, description.number) }; let scheme = Arc::clone(scheme::schemes().get(scheme_id).ok_or(Error::new(EBADFD))?); - let res = scheme.fmap(number, unsafe { &*(page.start_address().data() as *const Map) }); - - // Unmap Map user memory - current_space.write().munmap(page, page_count); + let res = scheme.fmap(number, map_dst); res } @@ -1354,6 +787,536 @@ impl KernelScheme for ProcScheme { _ => Err(Error::new(EBADF)), } } + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + // Don't hold a global lock during the context switch later on + let info = { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + handle.info.clone() + }; + + match info.operation { + Operation::Static(_) => { + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.static_data().expect("operations can't change"); + let src_buf = data.buf.get(data.offset..).unwrap_or(&[]); + + let len = buf.copy_common_bytes_from_slice(src_buf)?; + data.offset += len; + Ok(len) + }, + Operation::Memory { addrspace } => { + // Won't context switch, don't worry about the locks + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.mem_data().expect("operations can't change"); + + let mut bytes_read = 0; + + for chunk_opt in ptrace::context_memory(&mut *addrspace.write(), data.offset, buf.len()) { + let (chunk, _writable) = chunk_opt.ok_or(Error::new(EFAULT))?; + buf.advance(bytes_read).and_then(|buf| buf.limit(chunk.len())).ok_or(Error::new(EINVAL))?.copy_from_slice(unsafe { &*chunk })?; + /* + let dst_slice = &mut buf[bytes_read..bytes_read + chunk.len()]; + unsafe { + chunk.as_mut_ptr().copy_to_nonoverlapping(dst_slice.as_mut_ptr(), dst_slice.len()); + } + */ + bytes_read += chunk.len(); + } + + data.offset = VirtualAddress::new(data.offset.data() + bytes_read); + Ok(bytes_read) + }, + // TODO: Support reading only a specific address range. Maybe using seek? + Operation::AddrSpace { addrspace } => { + let mut handles = self.handles.write(); + let OperationData::Offset(ref mut offset) = handles.get_mut(&id).ok_or(Error::new(EBADF))?.data else { + return Err(Error::new(EBADFD)); + }; + + // TODO: Define a struct somewhere? + const RECORD_SIZE: usize = mem::size_of::() * 4; + let records = buf.in_exact_chunks(mem::size_of::()).array_chunks::<4>(); + + let addrspace = addrspace.read(); + let mut bytes_read = 0; + + for ([r1, r2, r3, r4], grant) in records.zip(addrspace.grants.iter()).skip(*offset / RECORD_SIZE) { + r1.write_usize(grant.start_address().data())?; + r2.write_usize(grant.size())?; + r3.write_usize(map_flags(grant.flags()).bits() | if grant.desc_opt.is_some() { 0x8000_0000 } else { 0 })?; + r4.write_usize(grant.desc_opt.as_ref().map_or(0, |d| d.offset))?; + bytes_read += RECORD_SIZE; + } + + *offset += bytes_read; + Ok(bytes_read) + } + + Operation::Regs(kind) => { + union Output { + float: FloatRegisters, + int: IntRegisters, + env: EnvRegisters, + } + + let (output, size) = match kind { + RegsKind::Float => with_context(info.pid, |context| { + // NOTE: The kernel will never touch floats + + Ok((Output { float: context.get_fx_regs() }, mem::size_of::())) + })?, + RegsKind::Int => try_stop_context(info.pid, |context| match unsafe { ptrace::regs_for(context) } { + None => { + assert!(!context.running, "try_stop_context is broken, clearly"); + println!("{}:{}: Couldn't read registers from stopped process", file!(), line!()); + Err(Error::new(ENOTRECOVERABLE)) + }, + Some(stack) => { + let mut regs = IntRegisters::default(); + stack.save(&mut regs); + Ok((Output { int: regs }, mem::size_of::())) + } + })?, + RegsKind::Env => { + ( + Output { env: self.read_env_regs(&info)? }, + mem::size_of::() + ) + } + }; + + let src_buf = unsafe { + slice::from_raw_parts(&output as *const _ as *const u8, size) + }; + + buf.copy_common_bytes_from_slice(src_buf) + }, + Operation::Trace => { + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.trace_data().expect("operations can't change"); + + // Wait for event + if handle.info.flags & O_NONBLOCK != O_NONBLOCK { + ptrace::wait(handle.info.pid)?; + } + + // Check if context exists + with_context(handle.info.pid, |_| Ok(()))?; + + let mut src_buf = [PtraceEvent::default(); 4]; + + // Read events + let src_len = src_buf.len(); + let slice = &mut src_buf[..core::cmp::min(src_len, buf.len() / mem::size_of::())]; + + let (read, reached) = ptrace::Session::with_session(info.pid, |session| { + let mut data = session.data.lock(); + Ok((data.recv_events(slice), data.is_reached())) + })?; + + // Save child processes in a list of processes to restart + for event in &slice[..read] { + if event.cause == PTRACE_EVENT_CLONE { + data.clones.push(ContextId::from(event.a)); + } + } + + // If there are no events, and breakpoint isn't reached, we + // must not have waited. + if read == 0 && !reached { + assert!(handle.info.flags & O_NONBLOCK == O_NONBLOCK, "wait woke up spuriously??"); + return Err(Error::new(EAGAIN)); + } + + for (dst, src) in buf.in_exact_chunks(mem::size_of::()).zip(slice.iter()) { + dst.copy_exactly(src)?; + } + + // Return read events + Ok(read * mem::size_of::()) + } + Operation::Name => read_from(buf, context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().name.as_bytes(), &mut 0), + Operation::Sigstack => read_from(buf, &context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.read().sigstack.unwrap_or(!0).to_ne_bytes(), &mut 0), + Operation::Attr(attr) => { + let src_buf = match (attr, &*Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?).read()) { + (Attr::Uid, context) => context.euid.to_string(), + (Attr::Gid, context) => context.egid.to_string(), + }.into_bytes(); + + read_from(buf, &src_buf, &mut 0) + } + Operation::Filetable { .. } => { + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.static_data().expect("operations can't change"); + + read_from(buf, &data.buf, &mut data.offset) + } + Operation::MmapMinAddr(ref addrspace) => { + buf.write_usize(addrspace.read().mmap_min)?; + Ok(mem::size_of::()) + } + Operation::SchedAffinity => { + buf.write_usize(context::contexts().get(info.pid).ok_or(Error::new(EBADFD))?.read().sched_affinity.map_or(usize::MAX, |a| a % crate::cpu_count()))?; + Ok(mem::size_of::()) + } + // TODO: Replace write() with SYS_DUP_FORWARD. + // TODO: Find a better way to switch address spaces, since they also require switching + // the instruction and stack pointer. Maybe remove `/regs` altogether and replace it + // with `/ctx` + _ => Err(Error::new(EBADF)), + } + } + fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + // Don't hold a global lock during the context switch later on + let info = { + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + handle.continue_ignored_children(); + handle.info.clone() + }; + + match info.operation { + Operation::Static(_) => Err(Error::new(EBADF)), + Operation::Memory { addrspace } => { + // Won't context switch, don't worry about the locks + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let data = handle.data.mem_data().expect("operations can't change"); + + let mut bytes_written = 0; + + for chunk_opt in ptrace::context_memory(&mut *addrspace.write(), data.offset, buf.len()) { + let (chunk, writable) = chunk_opt.ok_or(Error::new(EFAULT))?; + + if !writable { return Err(Error::new(EACCES)); } + + buf.advance(bytes_written).and_then(|buf| buf.limit(chunk.len())).ok_or(Error::new(EINVAL))? + .copy_to_slice(unsafe { &mut *chunk })?; + + bytes_written += chunk.len(); + } + + data.offset = data.offset.add(bytes_written); + Ok(bytes_written) + }, + Operation::AddrSpace { addrspace } => { + let mut chunks = buf.usizes(); + let mut words_read = 0; + let mut next = || { + words_read += 1; + chunks.next().ok_or(Error::new(EINVAL)) + }; + + match next()?? { + op @ ADDRSPACE_OP_MMAP | op @ ADDRSPACE_OP_TRANSFER => { + let fd = next()??; + let offset = next()??; + let (page, page_count) = crate::syscall::validate_region(next()??, next()??)?; + let flags = MapFlags::from_bits(next()??).ok_or(Error::new(EINVAL))?; + + if !flags.contains(MapFlags::MAP_FIXED) { + return Err(Error::new(EOPNOTSUPP)); + } + + let (scheme, number) = extract_scheme_number(fd)?; + + scheme.kfmap(number, &addrspace, &Map { offset, size: page_count * PAGE_SIZE, address: page.start_address().data(), flags }, op == ADDRSPACE_OP_TRANSFER)?; + } + ADDRSPACE_OP_MUNMAP => { + let (page, page_count) = crate::syscall::validate_region(next()??, next()??)?; + + addrspace.write().munmap(page, page_count); + } + ADDRSPACE_OP_MPROTECT => { + let (page, page_count) = crate::syscall::validate_region(next()??, next()??)?; + let flags = MapFlags::from_bits(next()??).ok_or(Error::new(EINVAL))?; + + addrspace.write().mprotect(page, page_count, flags)?; + } + _ => return Err(Error::new(EINVAL)), + } + Ok(words_read * mem::size_of::()) + } + Operation::Regs(kind) => match kind { + RegsKind::Float => { + let regs = unsafe { buf.read_exact::()? }; + + with_context_mut(info.pid, |context| { + // NOTE: The kernel will never touch floats + + // Ignore the rare case of floating point + // registers being uninitiated + let _ = context.set_fx_regs(regs); + + Ok(mem::size_of::()) + }) + }, + RegsKind::Int => { + let regs = unsafe { buf.read_exact::()? }; + + try_stop_context(info.pid, |context| match unsafe { ptrace::regs_for_mut(context) } { + None => { + println!("{}:{}: Couldn't read registers from stopped process", file!(), line!()); + Err(Error::new(ENOTRECOVERABLE)) + }, + Some(stack) => { + stack.load(®s); + + Ok(mem::size_of::()) + } + }) + } + RegsKind::Env => { + let regs = unsafe { buf.read_exact::()? }; + self.write_env_regs(&info, regs)?; + Ok(mem::size_of::()) + } + }, + Operation::Trace => { + let op = buf.read_u64()?; + let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?; + + // Set next breakpoint + ptrace::Session::with_session(info.pid, |session| { + session.data.lock().set_breakpoint( + Some(op) + .filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK)) + ); + Ok(()) + })?; + + if op.contains(PTRACE_STOP_SINGLESTEP) { + try_stop_context(info.pid, |context| { + match unsafe { ptrace::regs_for_mut(context) } { + None => { + println!("{}:{}: Couldn't read registers from stopped process", file!(), line!()); + Err(Error::new(ENOTRECOVERABLE)) + }, + Some(stack) => { + stack.set_singlestep(true); + Ok(()) + } + } + })?; + } + + // disable the ptrace_stop flag, which is used in some cases + with_context_mut(info.pid, |context| { + context.ptrace_stop = false; + Ok(()) + })?; + + // and notify the tracee's WaitCondition, which is used in other cases + ptrace::Session::with_session(info.pid, |session| { + session.tracee.notify(); + Ok(()) + })?; + + Ok(mem::size_of::()) + }, + Operation::Name => { + // TODO: What limit? + let mut name_buf = [0_u8; 256]; + let bytes_copied = buf.copy_common_bytes_to_slice(&mut name_buf)?; + + let utf8 = alloc::string::String::from_utf8(name_buf[..bytes_copied].to_vec()).map_err(|_| Error::new(EINVAL))?; + context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.write().name = utf8.into(); + Ok(buf.len()) + } + Operation::Sigstack => { + let sigstack = buf.read_usize()?; + context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?.write().sigstack = (sigstack != !0).then(|| sigstack); + Ok(buf.len()) + } + Operation::Attr(attr) => { + // TODO: What limit? + let mut str_buf = [0_u8; 32]; + let bytes_copied = buf.copy_common_bytes_to_slice(&mut str_buf)?; + + let id = core::str::from_utf8(&str_buf[..bytes_copied]).map_err(|_| Error::new(EINVAL))?.parse::().map_err(|_| Error::new(EINVAL))?; + let context_lock = Arc::clone(context::contexts().get(info.pid).ok_or(Error::new(ESRCH))?); + + match attr { + Attr::Uid => context_lock.write().euid = id, + Attr::Gid => context_lock.write().egid = id, + } + Ok(buf.len()) + } + Operation::Filetable { .. } => Err(Error::new(EBADF)), + + Operation::CurrentFiletable => { + let filetable_fd = buf.read_usize()?; + let (hopefully_this_scheme, number) = extract_scheme_number(filetable_fd)?; + + let filetable = hopefully_this_scheme.as_filetable(number)?; + + self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.info.operation = Operation::AwaitingFiletableChange(filetable); + + Ok(mem::size_of::()) + } + Operation::CurrentAddrSpace { .. } => { + let mut iter = buf.usizes(); + let addrspace_fd = iter.next().ok_or(Error::new(EINVAL))??; + let sp = iter.next().ok_or(Error::new(EINVAL))??; + let ip = iter.next().ok_or(Error::new(EINVAL))??; + + let (hopefully_this_scheme, number) = extract_scheme_number(addrspace_fd)?; + let space = hopefully_this_scheme.as_addrspace(number)?; + + self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.info.operation = Operation::AwaitingAddrSpaceChange { new: space, new_sp: sp, new_ip: ip }; + + Ok(3 * mem::size_of::()) + } + Operation::CurrentSigactions => { + let sigactions_fd = buf.read_usize()?; + let (hopefully_this_scheme, number) = extract_scheme_number(sigactions_fd)?; + let sigactions = hopefully_this_scheme.as_sigactions(number)?; + self.handles.write().get_mut(&id).ok_or(Error::new(EBADF))?.info.operation = Operation::AwaitingSigactionsChange(sigactions); + Ok(mem::size_of::()) + } + Operation::MmapMinAddr(ref addrspace) => { + let val = buf.read_usize()?; + if val % PAGE_SIZE != 0 || val > crate::USER_END_OFFSET { return Err(Error::new(EINVAL)); } + addrspace.write().mmap_min = val; + Ok(mem::size_of::()) + } + // TODO: Deduplicate code. + Operation::SchedAffinity => { + let val = buf.read_usize()?; + context::contexts().get(info.pid).ok_or(Error::new(EBADFD))?.write().sched_affinity = if val == usize::MAX { None } else { Some(val % crate::cpu_count()) }; + Ok(mem::size_of::()) + } + + _ => Err(Error::new(EBADF)), + } + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + let path = format!("proc:{}/{}", handle.info.pid.into(), match handle.info.operation { + Operation::Memory { .. } => "mem", + Operation::Regs(RegsKind::Float) => "regs/float", + Operation::Regs(RegsKind::Int) => "regs/int", + Operation::Regs(RegsKind::Env) => "regs/env", + Operation::Trace => "trace", + Operation::Static(path) => path, + Operation::Name => "name", + Operation::Sigstack => "sigstack", + Operation::Attr(Attr::Uid) => "uid", + Operation::Attr(Attr::Gid) => "gid", + Operation::Filetable { .. } => "filetable", + Operation::AddrSpace { .. } => "addrspace", + Operation::Sigactions(_) => "sigactions", + Operation::CurrentAddrSpace => "current-addrspace", + Operation::CurrentFiletable => "current-filetable", + Operation::CurrentSigactions => "current-sigactions", + Operation::OpenViaDup => "open-via-dup", + Operation::MmapMinAddr(_) => "mmap-min-addr", + Operation::SchedAffinity => "sched-affinity", + + _ => return Err(Error::new(EOPNOTSUPP)), + }); + + buf.copy_common_bytes_from_slice(path.as_bytes()) + } + fn kfstat(&self, id: usize, buffer: UserSliceWo) -> Result { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + buffer.copy_exactly(&Stat { + st_mode: MODE_FILE | 0o666, + st_size: match handle.data { + OperationData::Static(ref data) => (data.buf.len() - data.offset) as u64, + _ => 0, + }, + + ..Stat::default() + })?; + + Ok(0) + } + + /// Dup is currently used to implement clone() and execve(). + fn kdup(&self, old_id: usize, raw_buf: UserSliceRo, _: CallerCtx) -> Result { + let info = { + let handles = self.handles.read(); + let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?; + + handle.info.clone() + }; + + let handle = |operation, data| Handle { + info: Info { + flags: 0, + pid: info.pid, + operation, + }, + data, + }; + let mut array = [0_u8; 64]; + if raw_buf.len() > array.len() { + return Err(Error::new(EINVAL)); + } + raw_buf.copy_to_slice(&mut array[..raw_buf.len()])?; + let buf = &array[..raw_buf.len()]; + + self.new_handle(match info.operation { + Operation::OpenViaDup => { + let (uid, gid) = match &*context::contexts().current().ok_or(Error::new(ESRCH))?.read() { + context => (context.euid, context.egid), + }; + return self.open_inner(info.pid, Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?).filter(|s| !s.is_empty()), O_RDWR | O_CLOEXEC, uid, gid).map(OpenResult::SchemeLocal); + }, + + Operation::Filetable { ref filetable } => { + // TODO: Maybe allow userspace to either copy or transfer recently dupped file + // descriptors between file tables. + if buf != b"copy" { + return Err(Error::new(EINVAL)); + } + let new_filetable = Arc::try_new(RwLock::new(filetable.read().clone())).map_err(|_| Error::new(ENOMEM))?; + + handle(Operation::Filetable { filetable: new_filetable }, OperationData::Other) + } + Operation::AddrSpace { ref addrspace } => { + let (operation, is_mem) = match buf { + // TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But + // in that case, what scheme? + b"empty" => (Operation::AddrSpace { addrspace: new_addrspace()? }, false), + b"exclusive" => (Operation::AddrSpace { addrspace: addrspace.write().try_clone()? }, false), + b"mem" => (Operation::Memory { addrspace: Arc::clone(addrspace) }, true), + b"mmap-min-addr" => (Operation::MmapMinAddr(Arc::clone(addrspace)), false), + + grant_handle if grant_handle.starts_with(b"grant-") => { + let start_addr = usize::from_str_radix(core::str::from_utf8(&grant_handle[6..]).map_err(|_| Error::new(EINVAL))?, 16).map_err(|_| Error::new(EINVAL))?; + (Operation::GrantHandle { + description: Arc::clone(&addrspace.read().grants.contains(VirtualAddress::new(start_addr)).ok_or(Error::new(EINVAL))?.desc_opt.as_ref().ok_or(Error::new(EINVAL))?.desc.description) + }, false) + } + + _ => return Err(Error::new(EINVAL)), + }; + + handle(operation, if is_mem { OperationData::Memory(MemData { offset: VirtualAddress::new(0) }) } else { OperationData::Offset(0) }) + } + Operation::Sigactions(ref sigactions) => { + let new = match buf { + b"empty" => Context::empty_actions(), + b"copy" => Arc::new(RwLock::new(sigactions.read().clone())), + _ => return Err(Error::new(EINVAL)), + }; + handle(Operation::Sigactions(new), OperationData::Other) + } + _ => return Err(Error::new(EINVAL)), + }).map(OpenResult::SchemeLocal) + } + } extern "C" fn clone_handler() { let context_lock = Arc::clone(context::contexts().current().expect("expected the current context to be set in a spawn closure")); diff --git a/src/scheme/root.rs b/src/scheme/root.rs index f0932c989b..b8f31746d5 100644 --- a/src/scheme/root.rs +++ b/src/scheme/root.rs @@ -16,6 +16,7 @@ use crate::syscall::flag::{EventFlags, O_CREAT, MODE_FILE, MODE_DIR}; use crate::syscall::scheme::{calc_seek_offset_usize, Scheme}; use crate::scheme::{self, SchemeNamespace, SchemeId}; use crate::scheme::user::{UserInner, UserScheme}; +use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; struct FolderInner { data: Box<[u8]>, @@ -23,17 +24,14 @@ struct FolderInner { } impl FolderInner { - fn read(&self, buf: &mut [u8]) -> Result { - let mut i = 0; - let mut pos = self.pos.lock(); + fn read(&self, buf: UserSliceWo) -> Result { + let mut pos_guard = self.pos.lock(); - while i < buf.len() && *pos < self.data.len() { - buf[i] = self.data[*pos]; - i += 1; - *pos += 1; - } + let avail_buf = self.data.get(*pos_guard..).unwrap_or(&[]); + let bytes_read = buf.copy_common_bytes_from_slice(avail_buf)?; + *pos_guard += bytes_read; - Ok(i) + Ok(bytes_read) } fn seek(&self, pos: isize, whence: usize) -> Result { @@ -162,45 +160,6 @@ impl Scheme for RootScheme { } } - fn read(&self, file: usize, buf: &mut [u8]) -> Result { - let handle = { - let handles = self.handles.read(); - let handle = handles.get(&file).ok_or(Error::new(EBADF))?; - handle.clone() - }; - - match handle { - Handle::Scheme(inner) => { - inner.read(buf) - }, - Handle::File(_) => { - Err(Error::new(EBADF)) - }, - Handle::Folder(inner) => { - inner.read(buf) - } - } - } - - fn write(&self, file: usize, buf: &[u8]) -> Result { - let handle = { - let handles = self.handles.read(); - let handle = handles.get(&file).ok_or(Error::new(EBADF))?; - handle.clone() - }; - - match handle { - Handle::Scheme(inner) => { - inner.write(buf) - }, - Handle::File(_) => { - Err(Error::new(EBADF)) - }, - Handle::Folder(_) => { - Err(Error::new(EBADF)) - } - } - } fn seek(&self, file: usize, pos: isize, whence: usize) -> Result { let handle = { @@ -280,37 +239,6 @@ impl Scheme for RootScheme { Ok(i) } - fn fstat(&self, file: usize, stat: &mut Stat) -> Result { - let handle = { - let handles = self.handles.read(); - let handle = handles.get(&file).ok_or(Error::new(EBADF))?; - handle.clone() - }; - - match handle { - Handle::Scheme(_) => { - stat.st_mode = MODE_FILE; - stat.st_uid = 0; - stat.st_gid = 0; - stat.st_size = 0; - }, - Handle::File(_) => { - stat.st_mode = MODE_FILE; - stat.st_uid = 0; - stat.st_gid = 0; - stat.st_size = 0; - }, - Handle::Folder(inner) => { - stat.st_mode = MODE_DIR; - stat.st_uid = 0; - stat.st_gid = 0; - stat.st_size = inner.data.len() as u64; - } - } - - Ok(0) - } - fn fsync(&self, file: usize) -> Result { let handle = { let handles = self.handles.read(); @@ -344,4 +272,79 @@ impl Scheme for RootScheme { Ok(0) } } -impl crate::scheme::KernelScheme for RootScheme {} +impl crate::scheme::KernelScheme for RootScheme { + fn kread(&self, file: usize, buf: UserSliceWo) -> Result { + let handle = { + let handles = self.handles.read(); + let handle = handles.get(&file).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + match handle { + Handle::Scheme(inner) => { + inner.read(buf) + }, + Handle::File(_) => { + Err(Error::new(EBADF)) + }, + Handle::Folder(inner) => { + inner.read(buf) + } + } + } + + fn kwrite(&self, file: usize, buf: UserSliceRo) -> Result { + let handle = { + let handles = self.handles.read(); + let handle = handles.get(&file).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + match handle { + Handle::Scheme(inner) => { + inner.write(buf) + }, + Handle::File(_) => { + Err(Error::new(EBADF)) + }, + Handle::Folder(_) => { + Err(Error::new(EBADF)) + } + } + } + + fn kfstat(&self, file: usize, buf: UserSliceWo) -> Result { + let handle = { + let handles = self.handles.read(); + let handle = handles.get(&file).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + buf.copy_exactly(&match handle { + Handle::Scheme(_) => Stat { + st_mode: MODE_FILE, + st_uid: 0, + st_gid: 0, + st_size: 0, + ..Default::default() + }, + Handle::File(_) => Stat { + st_mode: MODE_FILE, + st_uid: 0, + st_gid: 0, + st_size: 0, + ..Default::default() + }, + Handle::Folder(inner) => Stat { + st_mode: MODE_DIR, + st_uid: 0, + st_gid: 0, + st_size: inner.data.len() as u64, + ..Default::default() + } + })?; + + Ok(0) + } + +} diff --git a/src/scheme/serio.rs b/src/scheme/serio.rs index ba0169d11b..354188baf9 100644 --- a/src/scheme/serio.rs +++ b/src/scheme/serio.rs @@ -9,6 +9,7 @@ use crate::scheme::*; use crate::sync::WaitQueue; use crate::syscall::flag::{EventFlags, EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK}; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::UserSliceWo; static SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default(); @@ -81,20 +82,6 @@ impl Scheme for SerioScheme { Ok(id) } - /// Read the file `number` into the `buffer` - /// - /// Returns the number of bytes read - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let handle = { - let handles = handles(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - INPUT[handle.index].call_once(init_input) - .receive_into(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "SerioScheme::read") - .ok_or(Error::new(EINTR)) - } - fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result { let mut handles = handles_mut(); if let Some(handle) = handles.get_mut(&id) { @@ -120,29 +107,6 @@ impl Scheme for SerioScheme { Ok(EventFlags::empty()) } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let handle = { - let handles = handles(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let mut i = 0; - let scheme_path = b"serio:"; - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - - let file_path = format!("{}", handle.index).into_bytes(); - let mut j = 0; - while i < buf.len() && j < file_path.len() { - buf[i] = file_path[j]; - j += 1; - } - - Ok(i) - } - fn fsync(&self, id: usize) -> Result { let _handle = { let handles = handles(); @@ -162,4 +126,24 @@ impl Scheme for SerioScheme { Ok(0) } } -impl crate::scheme::KernelScheme for SerioScheme {} +impl crate::scheme::KernelScheme for SerioScheme { + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + let handle = { + let handles = handles(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + INPUT[handle.index].call_once(init_input) + .receive_into_user(buf, handle.flags & O_NONBLOCK != O_NONBLOCK, "SerioScheme::read") + } + + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let handle = { + let handles = handles(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + let path = format!("serio:{}", handle.index).into_bytes(); + + buf.copy_common_bytes_from_slice(&path) + } +} diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index 5c304131bd..8baa72d41a 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -9,6 +9,7 @@ use crate::syscall::error::{Error, EBADF, ENOENT, Result}; use crate::syscall::flag::{MODE_DIR, MODE_FILE}; use crate::syscall::scheme::{calc_seek_offset_usize, Scheme}; use crate::arch::interrupt; +use crate::syscall::usercopy::UserSliceWo; mod block; mod context; @@ -106,20 +107,6 @@ impl Scheme for SysScheme { Err(Error::new(ENOENT)) } - fn read(&self, id: usize, buffer: &mut [u8]) -> Result { - let mut handles = self.handles.write(); - let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; - - let mut i = 0; - while i < buffer.len() && handle.seek < handle.data.len() { - buffer[i] = handle.data[handle.seek]; - i += 1; - handle.seek += 1; - } - - Ok(i) - } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { let mut handles = self.handles.write(); let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; @@ -129,40 +116,6 @@ impl Scheme for SysScheme { Ok(new_offset) } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - - let mut i = 0; - let scheme_path = b"sys:"; - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - - let path = handle.path.as_bytes(); - let mut j = 0; - while i < buf.len() && j < path.len() { - buf[i] = path[j]; - i += 1; - j += 1; - } - - Ok(i) - } - - fn fstat(&self, id: usize, stat: &mut Stat) -> Result { - let handles = self.handles.read(); - let handle = handles.get(&id).ok_or(Error::new(EBADF))?; - - stat.st_mode = handle.mode; - stat.st_uid = 0; - stat.st_gid = 0; - stat.st_size = handle.data.len() as u64; - - Ok(0) - } - fn fsync(&self, _id: usize) -> Result { Ok(0) } @@ -171,4 +124,45 @@ impl Scheme for SysScheme { self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0)) } } -impl crate::scheme::KernelScheme for SysScheme {} +impl crate::scheme::KernelScheme for SysScheme { + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + const FIRST: &[u8] = b"sys:"; + let mut bytes_read = buf.copy_common_bytes_from_slice(FIRST)?; + + if let Some(remaining) = buf.advance(FIRST.len()) { + bytes_read += remaining.copy_common_bytes_from_slice(handle.path.as_bytes())?; + } + + + Ok(bytes_read) + } + fn kread(&self, id: usize, buffer: UserSliceWo) -> Result { + let mut handles = self.handles.write(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + let avail_buf = handle.data.get(handle.seek..).unwrap_or(&[]); + + let byte_count = buffer.copy_common_bytes_from_slice(avail_buf)?; + + handle.seek = handle.seek.saturating_add(byte_count); + Ok(byte_count) + } + + fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + buf.copy_exactly(&Stat { + st_mode: handle.mode, + st_uid: 0, + st_gid: 0, + st_size: handle.data.len() as u64, + ..Default::default() + })?; + + Ok(0) + } +} diff --git a/src/scheme/time.rs b/src/scheme/time.rs index c82da9e292..1fbe272f45 100644 --- a/src/scheme/time.rs +++ b/src/scheme/time.rs @@ -1,5 +1,5 @@ use alloc::collections::BTreeMap; -use core::{mem, slice, str}; +use core::{mem, str}; use core::sync::atomic::{AtomicUsize, Ordering}; use spin::RwLock; @@ -9,6 +9,7 @@ use crate::syscall::data::TimeSpec; use crate::syscall::error::*; use crate::syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC, EventFlags}; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::{UserSliceWo, UserSliceRo}; use crate::time; pub struct TimeScheme { @@ -43,47 +44,6 @@ impl Scheme for TimeScheme { Ok(id) } - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - let clock = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let time_buf = unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut TimeSpec, buf.len()/mem::size_of::()) }; - - let mut i = 0; - while i < time_buf.len() { - let arch_time = match clock { - CLOCK_REALTIME => time::realtime(), - CLOCK_MONOTONIC => time::monotonic(), - _ => return Err(Error::new(EINVAL)) - }; - time_buf[i].tv_sec = (arch_time / time::NANOS_PER_SEC) as i64; - time_buf[i].tv_nsec = (arch_time % time::NANOS_PER_SEC) as i32; - i += 1; - } - - Ok(i * mem::size_of::()) - } - - fn write(&self, id: usize, buf: &[u8]) -> Result { - let clock = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let time_buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const TimeSpec, buf.len()/mem::size_of::()) }; - - let mut i = 0; - while i < time_buf.len() { - let time = time_buf[i]; - timeout::register(self.scheme_id, id, clock, time); - i += 1; - } - - Ok(i * mem::size_of::()) - } - fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { Ok(0) } @@ -93,21 +53,6 @@ impl Scheme for TimeScheme { handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(EventFlags::empty())) } - fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { - let clock = { - let handles = self.handles.read(); - *handles.get(&id).ok_or(Error::new(EBADF))? - }; - - let mut i = 0; - let scheme_path = format!("time:{}", clock).into_bytes(); - while i < buf.len() && i < scheme_path.len() { - buf[i] = scheme_path[i]; - i += 1; - } - Ok(i) - } - fn fsync(&self, id: usize) -> Result { let handles = self.handles.read(); handles.get(&id).ok_or(Error::new(EBADF)).and(Ok(0)) @@ -117,4 +62,61 @@ impl Scheme for TimeScheme { self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0)) } } -impl crate::scheme::KernelScheme for TimeScheme {} +impl crate::scheme::KernelScheme for TimeScheme { + fn kread(&self, id: usize, buf: UserSliceWo) -> Result { + let clock = { + let handles = self.handles.read(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + let mut bytes_read = 0; + + for current_chunk in buf.in_exact_chunks(mem::size_of::()) { + let arch_time = match clock { + CLOCK_REALTIME => time::realtime(), + CLOCK_MONOTONIC => time::monotonic(), + _ => return Err(Error::new(EINVAL)) + }; + let time = TimeSpec { + tv_sec: (arch_time / time::NANOS_PER_SEC) as i64, + tv_nsec: (arch_time % time::NANOS_PER_SEC) as i32, + }; + current_chunk.copy_exactly(&time)?; + + bytes_read += mem::size_of::(); + } + + Ok(bytes_read) + } + + fn kwrite(&self, id: usize, buf: UserSliceRo) -> Result { + let clock = { + let handles = self.handles.read(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + let mut bytes_written = 0; + + for current_chunk in buf.in_exact_chunks(mem::size_of::()) { + let time = unsafe { current_chunk.read_exact::()? }; + + timeout::register(self.scheme_id, id, clock, time); + + bytes_written += mem::size_of::(); + }; + + Ok(bytes_written) + } + fn kfpath(&self, id: usize, buf: UserSliceWo) -> Result { + let clock = { + let handles = self.handles.read(); + *handles.get(&id).ok_or(Error::new(EBADF))? + }; + + let scheme_path = format!("time:{}", clock).into_bytes(); + let byte_count = core::cmp::min(buf.len(), scheme_path.len()); + buf.limit(byte_count).expect("must succeed").copy_from_slice(&scheme_path)?; + Ok(byte_count) + } + +} diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 1f3f131eb6..391fe3159f 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -3,22 +3,24 @@ use alloc::boxed::Box; use alloc::collections::BTreeMap; use syscall::{SKMSG_FRETURNFD, CallerCtx}; use core::sync::atomic::{AtomicBool, Ordering}; -use core::{mem, slice, usize}; +use core::{mem, usize}; use core::convert::TryFrom; use spin::{Mutex, RwLock}; -use crate::context::{self, Context}; +use crate::context::{self, Context, BorrowedHtBuf}; use crate::context::file::{FileDescriptor, FileDescription}; use crate::context::memory::{AddrSpace, DANGLING, Grant, Region, GrantFileRef}; use crate::event; -use crate::paging::{PAGE_SIZE, mapper::InactiveFlusher, Page, round_down_pages, round_up_pages, VirtualAddress}; +use crate::paging::KernelMapper; +use crate::paging::{PAGE_SIZE, Page, VirtualAddress}; use crate::scheme::{AtomicSchemeId, SchemeId}; use crate::sync::{WaitQueue, WaitMap}; -use crate::syscall::data::{Map, Packet, Stat, StatVfs, TimeSpec}; +use crate::syscall::data::{Map, Packet}; use crate::syscall::error::*; -use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, MapFlags, PROT_READ, PROT_WRITE}; +use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, PROT_READ, PROT_WRITE}; use crate::syscall::number::*; use crate::syscall::scheme::Scheme; +use crate::syscall::usercopy::{UserSlice, UserSliceWo, UserSliceRo}; use super::{FileHandle, OpenResult, KernelScheme, current_caller_ctx}; @@ -119,136 +121,221 @@ impl UserInner { /// Map a readable structure to the scheme's userspace and return the /// pointer - pub fn capture(&self, buf: &[u8]) -> Result { + #[must_use = "copying back to head/tail buffers can fail"] + pub fn capture_user(&self, buf: UserSlice) -> Result> { UserInner::capture_inner( &self.context, - 0, - buf.as_ptr() as usize, - buf.len(), - PROT_READ, - None - ).map(|addr| addr.data()) + buf, + ) } + pub fn copy_and_capture_tail(&self, buf: &[u8]) -> Result> { + let dst_addr_space = Arc::clone(self.context.upgrade().ok_or(Error::new(ENODEV))?.read().addr_space()?); - /// Map a writeable structure to the scheme's userspace and return the - /// pointer - pub fn capture_mut(&self, buf: &mut [u8]) -> Result { - UserInner::capture_inner( - &self.context, - 0, - buf.as_mut_ptr() as usize, - buf.len(), - PROT_WRITE, - None - ).map(|addr| addr.data()) + let mut tail = BorrowedHtBuf::tail()?; + if buf.len() > tail.buf().len() { + return Err(Error::new(EINVAL)); + } + tail.buf_mut()[..buf.len()].copy_from_slice(buf); + + let src_page = Page::containing_address(VirtualAddress::new(tail.buf_mut().as_ptr() as usize)); + + let dst_page = dst_addr_space.write().mmap(None, 1, PROT_READ, |dst_page, flags, mapper, flusher| Ok(Grant::borrow(src_page, dst_page, 1, flags, None, &mut KernelMapper::lock(), mapper, flusher)?))?; + + Ok(CaptureGuard { + destroyed: false, + base: dst_page.start_address().data(), + len: buf.len(), + space: None, + head: CopyInfo { + src: Some(tail), + dst: None, + }, + tail: CopyInfo { src: None, dst: None }, + }) } // TODO: Use an address space Arc over a context Arc. While contexts which share address spaces // still can access borrowed scheme pages, it would both be cleaner and would handle the case // where the initial context is closed. - fn capture_inner(context_weak: &Weak>, dst_address: usize, address: usize, size: usize, flags: MapFlags, desc_opt: Option) - -> Result { - if size == 0 { - // NOTE: Rather than returning NULL, we return a dummy dangling address, that is also - // non-canonical on x86. This means that scheme handlers do not need to check the - // length before creating a Rust slice (which cannot have NULL as address regardless of - // the length; this actually made nulld think that an empty path was invalid UTF-8 - // because of enum layout optimization), independent of whatever alignment this slice - // will have. Additionally, they would generate a general protection fault immediately - // if they ever tried to access this dangling address. + /// Capture a buffer owned by userspace, mapping it contiguously onto scheme memory. + // TODO: Hypothetical accept_head_leak, accept_tail_leak options might be useful for + // libc-controlled buffer pools. + fn capture_inner(context_weak: &Weak>, user_buf: UserSlice) -> Result> { + let (mode, map_flags) = match (READ, WRITE) { + (true, false) => (Mode::Ro, PROT_READ), + (false, true) => (Mode::Wo, PROT_WRITE), - // Set the most significant bit. - return Ok(VirtualAddress::new(DANGLING)); + _ => unreachable!(), + }; + if user_buf.is_empty() { + // NOTE: Rather than returning NULL, we return a dummy dangling address, that is + // happens to be non-canonical on x86. This relieves scheme handlers from having to + // check the length before e.g. creating nonnull Rust references (when an empty length + // still requires a nonnull but possibly dangling pointer, and this has in practice + // made nulld errorneously confuse an empty Some("") with None (invalid UTF-8), due to + // enum layout optimization, as the pointer was null and not dangling). A good choice + // is thus to simply set the most-significant bit to be compatible with all alignments. + return Ok(CaptureGuard { + destroyed: false, + base: DANGLING, + len: 0, + space: None, + head: CopyInfo { src: None, dst: None }, + tail: CopyInfo { src: None, dst: None }, + }); } - let src_page = Page::containing_address(VirtualAddress::new(round_down_pages(address))); - let offset = address - src_page.start_address().data(); - let page_count = round_up_pages(offset + size) / PAGE_SIZE; - let requested_dst_page = (dst_address != 0).then_some(Page::containing_address(VirtualAddress::new(round_down_pages(dst_address)))); - - let dst_space_lock = Arc::clone(context_weak.upgrade().ok_or(Error::new(ESRCH))?.read().addr_space()?); let cur_space_lock = AddrSpace::current()?; + let dst_space_lock = Arc::clone(context_weak.upgrade().ok_or(Error::new(ESRCH))?.read().addr_space()?); - //TODO: Use syscall_head and syscall_tail to avoid leaking data - let dst_page = if Arc::ptr_eq( - &dst_space_lock, - &cur_space_lock, - ) { - let mut dst_space = dst_space_lock.write(); - dst_space.mmap(requested_dst_page, page_count, flags, |dst_page, page_flags, mapper, flusher| { - //TODO: remove hack to use same mapper for borrow - let src_mapper = unsafe { &mut *(mapper as *mut _) }; - let dst_mapper = unsafe { &mut *(mapper as *mut _) }; - Ok(Grant::borrow(src_page, dst_page, page_count, page_flags, desc_opt, src_mapper, dst_mapper, flusher)?) - })? - } else { - let mut dst_space = dst_space_lock.write(); - dst_space.mmap(requested_dst_page, page_count, flags, move |dst_page, page_flags, mapper, flusher| { - let mut cur_space = cur_space_lock.write(); - Ok(Grant::borrow(src_page, dst_page, page_count, page_flags, desc_opt, &mut cur_space.table.utable, mapper, flusher)?) - })? - }; - - Ok(dst_page.start_address().add(offset)) - } - - pub fn release(&self, address: usize) -> Result<()> { - if address == DANGLING { - return Ok(()); + if Arc::ptr_eq(&dst_space_lock, &cur_space_lock) { + // Same address space, no need to remap anything! + return Ok(CaptureGuard { + destroyed: false, + base: user_buf.addr(), + len: user_buf.len(), + space: None, + head: CopyInfo { src: None, dst: None }, + tail: CopyInfo { src: None, dst: None }, + }); } - let context_lock = self.context.upgrade().ok_or(Error::new(ESRCH))?; - let context = context_lock.write(); - let mut addr_space = context.addr_space()?.write(); + let (src_page, page_count, offset) = page_range_containing(user_buf.addr(), user_buf.len()); - let region = match addr_space.grants.contains(VirtualAddress::new(address)).map(Region::from) { - Some(region) => region, - None => return Err(Error::new(EFAULT)), + let align_offset = if offset == 0 { 0 } else { PAGE_SIZE - offset }; + let (head_part_of_buf, middle_tail_part_of_buf) = user_buf + .split_at(core::cmp::min(align_offset, user_buf.len())) + .expect("split must succeed"); + + let mut dst_space = dst_space_lock.write(); + + let free_region = dst_space.grants.find_free(dst_space.mmap_min, page_count * PAGE_SIZE).ok_or(Error::new(ENOMEM))?; + + let first_dst_page = Page::containing_address(free_region.start_address()); + + let head = if !head_part_of_buf.is_empty() { + // FIXME: Signal context can probably recursively use head/tail. + let mut array = BorrowedHtBuf::head()?; + + let len = core::cmp::min(PAGE_SIZE - offset, user_buf.len()); + + match mode { + Mode::Ro => { + array.buf_mut()[..offset].fill(0_u8); + array.buf_mut()[offset + len..].fill(0_u8); + + let slice = &mut array.buf_mut()[offset..][..len]; + let head_part_of_buf = user_buf.limit(len).expect("always smaller than max len"); + + head_part_of_buf.reinterpret_unchecked::().copy_to_slice(slice)?; + } + Mode::Wo => { + array.buf_mut().fill(0_u8); + } + } + let head_buf_page = Page::containing_address(VirtualAddress::new(array.buf_mut().as_mut_ptr() as usize)); + + dst_space.mmap(Some(first_dst_page), 1, map_flags, move |dst_page, page_flags, mapper, flusher| { + Ok(Grant::borrow(head_buf_page, dst_page, 1, page_flags, None, &mut KernelMapper::lock(), mapper, flusher)?) + })?; + + let head = CopyInfo { + src: Some(array), + dst: (mode == Mode::Wo).then_some(head_part_of_buf.reinterpret_unchecked()), + }; + + head + } else { + CopyInfo { + src: None, + dst: None, + } }; - addr_space.grants.take(®ion).unwrap().unmap(&mut addr_space.table.utable, InactiveFlusher::new()); - Ok(()) + let (first_middle_dst_page, first_middle_src_page) = if !head_part_of_buf.is_empty() { (first_dst_page.next(), src_page.next()) } else { (first_dst_page, src_page) }; + + let middle_page_count = middle_tail_part_of_buf.len() / PAGE_SIZE; + let tail_size = middle_tail_part_of_buf.len() % PAGE_SIZE; + + let (_middle_part_of_buf, tail_part_of_buf) = middle_tail_part_of_buf.split_at(middle_page_count * PAGE_SIZE).expect("split must succeed"); + + if middle_page_count > 0 { + dst_space.mmap(Some(first_middle_dst_page), middle_page_count, map_flags, move |dst_page, page_flags, mapper, flusher| { + let mut cur_space = cur_space_lock.write(); + Ok(Grant::borrow(first_middle_src_page, dst_page, middle_page_count, page_flags, None, &mut cur_space.table.utable, mapper, flusher)?) + })?; + } + + let tail = if !tail_part_of_buf.is_empty() { + let tail_dst_page = first_middle_dst_page.next_by(middle_page_count); + + // FIXME: Signal context can probably recursively use head/tail. + let mut array = BorrowedHtBuf::tail()?; + + let tail_buf_page = Page::containing_address(VirtualAddress::new(array.buf_mut().as_mut_ptr() as usize)); + + match mode { + Mode::Ro => { + let (to_copy, to_zero) = array.buf_mut().split_at_mut(tail_size); + + to_zero.fill(0_u8); + + // FIXME: remove reinterpret_unchecked + tail_part_of_buf.reinterpret_unchecked::().copy_to_slice(to_copy)?; + } + Mode::Wo => { + array.buf_mut().fill(0_u8); + } + } + + dst_space.mmap(Some(tail_dst_page), 1, map_flags, move |dst_page, page_flags, mapper, flusher| { + Ok(Grant::borrow(tail_buf_page, dst_page, 1, page_flags, None, &mut KernelMapper::lock(), mapper, flusher)?) + })?; + + CopyInfo { + src: Some(array), + dst: (mode == Mode::Wo).then_some(tail_part_of_buf.reinterpret_unchecked()), + } + } else { + CopyInfo { + src: None, + dst: None, + } + }; + + drop(dst_space); + + Ok(CaptureGuard { + destroyed: false, + base: free_region.start_address().data() + offset, + len: user_buf.len(), + space: Some(dst_space_lock), + head, + tail, + }) } - pub fn read(&self, buf: &mut [u8]) -> Result { - let packet_buf = unsafe { slice::from_raw_parts_mut( - buf.as_mut_ptr() as *mut Packet, - buf.len()/mem::size_of::()) - }; - + pub fn read(&self, buf: UserSliceWo) -> Result { // If O_NONBLOCK is used, do not block let nonblock = self.flags & O_NONBLOCK == O_NONBLOCK; // If unmounting, do not block so that EOF can be returned immediately let block = !(nonblock || self.unmounting.load(Ordering::SeqCst)); - if let Some(count) = self.todo.receive_into(packet_buf, block, "UserInner::read") { - if count > 0 { - // If we received requests, return them to the scheme handler - Ok(count * mem::size_of::()) - } else if self.unmounting.load(Ordering::SeqCst) { - // If there were no requests and we were unmounting, return EOF - Ok(0) - } else { - // If there were no requests and O_NONBLOCK was used, return EAGAIN - Err(Error::new(EAGAIN)) - } - } else if self.unmounting.load(Ordering::SeqCst) { - // If we are unmounting and there are no pending requests, return EOF - // Unmounting is read again because the previous value - // may have changed since we first blocked for packets - Ok(0) - } else { - // A signal was received, return EINTR - Err(Error::new(EINTR)) + + match self.todo.receive_into_user(buf, block, "UserInner::read") { + // If we received requests, return them to the scheme handler + Ok(byte_count) => Ok(byte_count), + // If there were no requests and we were unmounting, return EOF + Err(Error { errno: EAGAIN }) if self.unmounting.load(Ordering::SeqCst) => Ok(0), + // If there were no requests and O_NONBLOCK was used (EAGAIN), or some other error + // occurred, return that. + Err(error) => Err(error), } } - pub fn write(&self, buf: &[u8]) -> Result { - // TODO: Alignment - - let packets = unsafe { core::slice::from_raw_parts(buf.as_ptr().cast::(), buf.len() / mem::size_of::()) }; + pub fn write(&self, buf: UserSliceRo) -> Result { let mut packets_read = 0; - for packet in packets { - match self.handle_packet(packet) { + for chunk in buf.in_exact_chunks(mem::size_of::()) { + match self.handle_packet(&unsafe { chunk.read_exact::()? }) { Ok(()) => packets_read += 1, Err(_) if packets_read > 0 => break, Err(error) => return Err(error), @@ -259,6 +346,7 @@ impl UserInner { } fn handle_packet(&self, packet: &Packet) -> Result<()> { if packet.id == 0 { + // TODO: Simplify logic by using SKMSG with packet.id being ignored? match packet.a { SYS_FEVENT => event::trigger(self.scheme_id.load(Ordering::SeqCst), packet.b, EventFlags::from_bits_truncate(packet.c)), _ => log::warn!("Unknown scheme -> kernel message {}", packet.a) @@ -277,6 +365,9 @@ impl UserInner { _ => return Err(Error::new(EINVAL)), } } else { + // TODO: Avoid having to check if fmap contains the packet ID, by forcing fmap to use + // SKMSG? + let mut retcode = packet.a; // The motivation of doing this here instead of within the fmap handler, is that we @@ -284,27 +375,36 @@ impl UserInner { // from two (context switch + active TLB flush) to one (context switch). if let Some((context_weak, desc, map)) = self.fmap.lock().remove(&packet.id) { if let Ok(address) = Error::demux(packet.a) { - if address % PAGE_SIZE > 0 { - log::warn!("scheme returned unaligned address, causing extra frame to be allocated"); + if address % PAGE_SIZE > 0 || map.size % PAGE_SIZE > 0 { + return Err(Error::new(EINVAL)); } + let src_page = Page::containing_address(VirtualAddress::new(address)); + let dst_page = Some(map.address).filter(|addr| *addr > 0).map(|addr| Page::containing_address(VirtualAddress::new(addr))); + let file_ref = GrantFileRef { desc, offset: map.offset, flags: map.flags }; - let res = UserInner::capture_inner(&context_weak, map.address, address, map.size, map.flags, Some(file_ref)); - if let Ok(grant_address) = res { - if let Some(context_lock) = context_weak.upgrade() { - let context = context_lock.read(); - let mut addr_space = context.addr_space()?.write(); - //TODO: ensure all mappings are aligned! - let map_pages = (map.size + PAGE_SIZE - 1) / PAGE_SIZE; + + if let Some(context_lock) = context_weak.upgrade() { + let context = context_lock.read(); + let mut addr_space = context.addr_space()?.write(); + + // TODO: ensure all mappings are aligned! + let page_count = map.size.div_ceil(PAGE_SIZE); + + let res = addr_space.mmap(dst_page, page_count, map.flags, move |dst_page, flags, mapper, flusher| { + Ok(Grant::borrow(src_page, dst_page, page_count, flags, Some(file_ref), &mut AddrSpace::current()?.write().table.utable, mapper, flusher)?) + }); + retcode = Error::mux(res.map(|grant_start_page| { addr_space.grants.funmap.insert( - Region::new(grant_address, map_pages * PAGE_SIZE), - VirtualAddress::new(address) + Region::new(grant_start_page.start_address(), page_count * PAGE_SIZE), + VirtualAddress::new(address), ); - } else { - //TODO: packet.pid is an assumption - println!("UserInner::write: failed to find context {} for fmap", packet.pid); - } + grant_start_page.start_address().data() + })); + + } else { + //TODO: packet.pid is an assumption + log::warn!("UserInner::write: failed to find context {} for fmap", packet.pid); } - retcode = Error::mux(res.map(|addr| addr.data())); } else { let _ = desc.close(); } @@ -325,12 +425,16 @@ impl UserInner { } fn fmap_inner(&self, file: usize, map: &Map) -> Result { + if map.address % PAGE_SIZE != 0 { + return Err(Error::new(EINVAL)); + } + if map.size % PAGE_SIZE != 0 { + log::warn!("fmap passed length {:#0x} instead of {:#0x}", map.size, map.size.next_multiple_of(PAGE_SIZE)); + } + let (pid, uid, gid, context_weak, desc) = { - let context_lock = Arc::clone(context::contexts().current().ok_or(Error::new(ESRCH))?); + let context_lock = context::current()?; let context = context_lock.read(); - if map.size % PAGE_SIZE != 0 { - log::warn!("Unaligned map size for context `{}`", context.name); - } // TODO: Faster, cleaner mechanism to get descriptor let scheme = self.scheme_id.load(Ordering::SeqCst); let mut desc_res = Err(Error::new(EBADF)); @@ -348,11 +452,18 @@ impl UserInner { (context.id, context.euid, context.egid, Arc::downgrade(&context_lock), desc) }; - let address = self.capture(map)?; + let aligned_size_map = Map { + offset: map.offset, + size: map.size.next_multiple_of(PAGE_SIZE), + address: map.address, + flags: map.flags, + }; + + let address = self.copy_and_capture_tail(&aligned_size_map)?; let id = self.next_id(); - self.fmap.lock().insert(id, (context_weak, desc, *map)); + self.fmap.lock().insert(id, (context_weak, desc, aligned_size_map)); let result = self.call_extended_inner(Packet { id, @@ -361,12 +472,10 @@ impl UserInner { gid, a: SYS_FMAP, b: file, - c: address, - d: mem::size_of::() + c: address.base(), + d: address.len(), }); - let _ = self.release(address); - result.and_then(|response| match response { Response::Regular(code) => Error::demux(code), Response::Fd(_) => { @@ -377,6 +486,72 @@ impl UserInner { }) } } +pub struct CaptureGuard { + destroyed: bool, + base: usize, + len: usize, + + space: Option>>, + + head: CopyInfo, + tail: CopyInfo, +} +impl CaptureGuard { + fn base(&self) -> usize { self.base } + fn len(&self) -> usize { self.len } +} +struct CopyInfo { + src: Option, + + // TODO + dst: Option>, +} +impl CaptureGuard { + fn release_inner(&mut self) -> Result<()> { + if self.destroyed { + return Ok(()); + } + self.destroyed = true; + + if self.base == DANGLING { + return Ok(()); + } + + let mut result = Ok(()); + + // TODO: Encode src and dst better using const generics. + if let CopyInfo { src: Some(ref src), dst: Some(ref mut dst) } = self.head { + result = result.and_then(|()| dst.copy_from_slice(&src.buf()[self.base % PAGE_SIZE..][..dst.len()])); + } + if let CopyInfo { src: Some(ref src), dst: Some(ref mut dst) } = self.tail { + result = result.and_then(|()| dst.copy_from_slice(&src.buf()[..dst.len()])); + } + let Some(space) = self.space.take() else { + return result; + }; + + let (first_page, page_count, _offset) = page_range_containing(self.base, self.len); + + space.write().munmap(first_page, page_count); + + result + } + pub fn release(mut self) -> Result<()> { + self.release_inner() + } +} +impl Drop for CaptureGuard { + fn drop(&mut self) { + let _ = self.release_inner(); + } +} +/// base..base+size => page..page+page_count*PAGE_SIZE, offset +fn page_range_containing(base: usize, size: usize) -> (Page, usize, usize) { + let first_page = Page::containing_address(VirtualAddress::new(base)); + let offset = base - first_page.start_address().data(); + + (first_page, (size + offset).div_ceil(PAGE_SIZE), offset) +} /// `UserInner` has to be wrapped pub struct UserScheme { @@ -406,38 +581,14 @@ impl Scheme for UserScheme { fn rmdir(&self, path: &str, _uid: u32, _gid: u32) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture(path.as_bytes())?; - let result = inner.call(SYS_RMDIR, address, path.len(), 0); - let _ = inner.release(address); - result + let address = inner.copy_and_capture_tail(path.as_bytes())?; + inner.call(SYS_RMDIR, address.base(), address.len(), 0) } fn unlink(&self, path: &str, _uid: u32, _gid: u32) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture(path.as_bytes())?; - let result = inner.call(SYS_UNLINK, address, path.len(), 0); - let _ = inner.release(address); - result - } - - fn dup(&self, old_id: usize, buf: &[u8]) -> Result { - self.kdup(old_id, buf, current_caller_ctx()?).and_then(handle_open_res) - } - - fn read(&self, file: usize, buf: &mut [u8]) -> Result { - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture_mut(buf)?; - let result = inner.call(SYS_READ, file, address, buf.len()); - let _ = inner.release(address); - result - } - - fn write(&self, file: usize, buf: &[u8]) -> Result { - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture(buf)?; - let result = inner.call(SYS_WRITE, file, address, buf.len()); - let _ = inner.release(address); - result + let address = inner.copy_and_capture_tail(path.as_bytes())?; + inner.call(SYS_UNLINK, address.base(), address.len(), 0) } fn seek(&self, file: usize, position: isize, whence: usize) -> Result { @@ -526,36 +677,10 @@ impl Scheme for UserScheme { } } - fn fpath(&self, file: usize, buf: &mut [u8]) -> Result { - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture_mut(buf)?; - let result = inner.call(SYS_FPATH, file, address, buf.len()); - let _ = inner.release(address); - result - } - fn frename(&self, file: usize, path: &str, _uid: u32, _gid: u32) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture(path.as_bytes())?; - let result = inner.call(SYS_FRENAME, file, address, path.len()); - let _ = inner.release(address); - result - } - - fn fstat(&self, file: usize, stat: &mut Stat) -> Result { - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture_mut(stat)?; - let result = inner.call(SYS_FSTAT, file, address, mem::size_of::()); - let _ = inner.release(address); - result - } - - fn fstatvfs(&self, file: usize, stat: &mut StatVfs) -> Result { - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture_mut(stat)?; - let result = inner.call(SYS_FSTATVFS, file, address, mem::size_of::()); - let _ = inner.release(address); - result + let address = inner.copy_and_capture_tail(path.as_bytes())?; + inner.call(SYS_FRENAME, file, address.base(), address.len()) } fn fsync(&self, file: usize) -> Result { @@ -568,15 +693,6 @@ impl Scheme for UserScheme { inner.call(SYS_FTRUNCATE, file, len, 0) } - fn futimens(&self, file: usize, times: &[TimeSpec]) -> Result { - let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let buf = unsafe { slice::from_raw_parts(times.as_ptr() as *const u8, mem::size_of::() * times.len()) }; - let address = inner.capture(buf)?; - let result = inner.call(SYS_FUTIMENS, file, address, buf.len()); - let _ = inner.release(address); - result - } - fn close(&self, file: usize) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; inner.call(SYS_CLOSE, file, 0, 0) @@ -585,24 +701,72 @@ impl Scheme for UserScheme { impl KernelScheme for UserScheme { fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture(path.as_bytes())?; - let result = inner.call_extended(ctx, [SYS_OPEN, address, path.len(), flags]); - let _ = inner.release(address); + let address = inner.copy_and_capture_tail(path.as_bytes())?; + match inner.call_extended(ctx, [SYS_OPEN, address.base(), address.len(), flags])? { + Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal), + Response::Fd(desc) => Ok(OpenResult::External(desc)), + } + } + fn kdup(&self, file: usize, buf: UserSliceRo, ctx: CallerCtx) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let address = inner.capture_user(buf)?; + let result = inner.call_extended(ctx, [SYS_DUP, file, address.base(), address.len()]); + + address.release()?; match result? { Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal), Response::Fd(desc) => Ok(OpenResult::External(desc)), } } - fn kdup(&self, file: usize, buf: &[u8], ctx: CallerCtx) -> Result { + fn kfpath(&self, file: usize, buf: UserSliceWo) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - let address = inner.capture(buf)?; - let result = inner.call_extended(ctx, [SYS_DUP, file, address, buf.len()]); - let _ = inner.release(address); + let address = inner.capture_user(buf)?; + let result = inner.call(SYS_FPATH, file, address.base(), address.len()); + address.release()?; + result + } - match result? { - Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal), - Response::Fd(desc) => Ok(OpenResult::External(desc)), - } + fn kread(&self, file: usize, buf: UserSliceWo) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let address = inner.capture_user(buf)?; + let result = inner.call(SYS_READ, file, address.base(), address.len()); + address.release()?; + result + } + + fn kwrite(&self, file: usize, buf: UserSliceRo) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let address = inner.capture_user(buf)?; + let result = inner.call(SYS_WRITE, file, address.base(), address.len()); + address.release()?; + result + } + fn kfutimens(&self, file: usize, buf: UserSliceRo) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let address = inner.capture_user(buf)?; + let result = inner.call(SYS_FUTIMENS, file, address.base(), address.len()); + address.release()?; + result + } + fn kfstat(&self, file: usize, stat: UserSliceWo) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let address = inner.capture_user(stat)?; + let result = inner.call(SYS_FSTAT, file, address.base(), address.len()); + address.release()?; + result + } + fn kfstatvfs(&self, file: usize, stat: UserSliceWo) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + let address = inner.capture_user(stat)?; + let result = inner.call(SYS_FSTATVFS, file, address.base(), address.len()); + address.release()?; + result } } + +#[derive(PartialEq)] +pub enum Mode { + Ro, + Wo, +} diff --git a/src/sync/wait_queue.rs b/src/sync/wait_queue.rs index d20d581e8a..7eabafe1a4 100644 --- a/src/sync/wait_queue.rs +++ b/src/sync/wait_queue.rs @@ -1,7 +1,10 @@ use alloc::collections::VecDeque; use spin::Mutex; +use syscall::{EAGAIN, EINTR}; use crate::sync::WaitCondition; +use crate::syscall::usercopy::UserSliceWo; +use crate::syscall::error::{Error, EINVAL, Result}; #[derive(Debug)] pub struct WaitQueue { @@ -40,6 +43,42 @@ impl WaitQueue { } } + pub fn receive_into_user(&self, buf: UserSliceWo, block: bool, reason: &'static str) -> Result { + loop { + let mut inner = self.inner.lock(); + + if inner.is_empty() { + if block { + if !self.condition.wait(inner, reason) { + return Err(Error::new(EINTR)); + } + continue; + } else if buf.is_empty() { + return Ok(0); + } else if buf.len() < core::mem::size_of::() { + return Err(Error::new(EINVAL)); + } else { + // TODO: EWOULDBLOCK? + return Err(Error::new(EAGAIN)); + } + } + + let (s1, s2) = inner.as_slices(); + let s1_bytes = unsafe { core::slice::from_raw_parts(s1.as_ptr().cast::(), s1.len() * core::mem::size_of::()) }; + let s2_bytes = unsafe { core::slice::from_raw_parts(s2.as_ptr().cast::(), s2.len() * core::mem::size_of::()) }; + + let mut bytes_copied = buf.copy_common_bytes_from_slice(s1_bytes)?; + + if let Some(buf_for_s2) = buf.advance(s1_bytes.len()) { + bytes_copied += buf_for_s2.copy_common_bytes_from_slice(s2_bytes)?; + } + + let _ = inner.drain(..bytes_copied / core::mem::size_of::()); + + return Ok(bytes_copied); + } + } + pub fn receive_into(&self, buf: &mut [T], block: bool, reason: &'static str) -> Option { let mut i = 0; diff --git a/src/syscall/debug.rs b/src/syscall/debug.rs index 7927a83e75..b59dcc43fc 100644 --- a/src/syscall/debug.rs +++ b/src/syscall/debug.rs @@ -1,10 +1,13 @@ use core::{ascii, mem}; use alloc::string::String; +use alloc::vec::Vec; use super::data::{Map, Stat, TimeSpec}; -use super::flag::*; +use super::{flag::*, copy_path_to_buf}; use super::number::*; -use super::validate::*; +use super::usercopy::UserSlice; + +use crate::syscall::error::Result; struct ByteStr<'a>(&'a[u8]); @@ -20,22 +23,37 @@ impl<'a> ::core::fmt::Debug for ByteStr<'a> { Ok(()) } } +fn debug_path(ptr: usize, len: usize) -> Result { + // TODO: PATH_MAX + UserSlice::ro(ptr, len).and_then(|slice| copy_path_to_buf(slice, 4096)) +} +fn debug_buf(ptr: usize, len: usize) -> Result> { + UserSlice::ro(ptr, len).and_then(|user| { + let mut buf = vec! [0_u8; 4096]; + let count = user.copy_common_bytes_to_slice(&mut buf)?; + buf.truncate(count); + Ok(buf) + }) +} +unsafe fn read_struct(ptr: usize) -> Result { + UserSlice::ro(ptr, mem::size_of::()).and_then(|slice| slice.read_exact::()) +} //TODO: calling format_call with arguments from another process space will not work pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> String { match a { SYS_OPEN => format!( "open({:?}, {:#X})", - validate_slice(b as *const u8, c).map(ByteStr), + debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())), d ), SYS_RMDIR => format!( "rmdir({:?})", - validate_slice(b as *const u8, c).map(ByteStr) + debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())), ), SYS_UNLINK => format!( "unlink({:?})", - validate_slice(b as *const u8, c).map(ByteStr) + debug_path(b, c).as_ref().map(|p| ByteStr(p.as_bytes())), ), SYS_CLOSE => format!( "close({})", b @@ -43,13 +61,13 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_DUP => format!( "dup({}, {:?})", b, - validate_slice(c as *const u8, d).map(ByteStr) + debug_buf(c, d).as_ref().map(|b| ByteStr(&*b)), ), SYS_DUP2 => format!( "dup2({}, {}, {:?})", b, c, - validate_slice(d as *const u8, e).map(ByteStr) + debug_buf(d, e).as_ref().map(|b| ByteStr(&*b)), ), SYS_READ => format!( "read({}, {:#X}, {})", @@ -103,10 +121,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_FMAP => format!( "fmap({}, {:?})", b, - validate_slice( - c as *const Map, - d/mem::size_of::() - ), + UserSlice::ro(c, d).and_then(|buf| unsafe { buf.read_exact::() }), ), SYS_FUNMAP => format!( "funmap({:#X}, {:#X})", @@ -122,15 +137,12 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_FRENAME => format!( "frename({}, {:?})", b, - validate_slice(c as *const u8, d).map(ByteStr), + debug_path(c, d), ), SYS_FSTAT => format!( "fstat({}, {:?})", b, - validate_slice( - c as *const Stat, - d/mem::size_of::() - ), + UserSlice::ro(c, d).and_then(|buf| unsafe { buf.read_exact::() }), ), SYS_FSTATVFS => format!( "fstatvfs({}, {:#X}, {})", @@ -150,16 +162,21 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_FUTIMENS => format!( "futimens({}, {:?})", b, - validate_slice( - c as *const TimeSpec, - d/mem::size_of::() - ), + UserSlice::ro(c, d).and_then(|buf| { + let mut times = vec! [unsafe { buf.read_exact::()? }]; + + // One or two timespecs + if let Some(second) = buf.advance(mem::size_of::()) { + times.push(unsafe { second.read_exact::()? }); + } + Ok(times) + }), ), SYS_CLOCK_GETTIME => format!( "clock_gettime({}, {:?})", b, - validate_slice_mut(c as *mut TimeSpec, 1) + unsafe { read_struct::(c) } ), SYS_EXIT => format!( "exit({})", @@ -168,7 +185,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_FUTEX => format!( "futex({:#X} [{:?}], {}, {}, {}, {})", b, - validate_slice_mut(b as *mut i32, 1).map(|uaddr| &mut uaddr[0]), + UserSlice::ro(b, 4).and_then(|buf| buf.read_u32()), c, d, e, @@ -203,12 +220,17 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - SYS_SIGPROCMASK => format!( "sigprocmask({}, {:?}, {:?})", b, - validate_slice(c as *const [u64; 2], 1), - validate_slice(d as *const [u64; 2], 1) + unsafe { read_struct::<[u64; 2]>(c) }, + unsafe { read_struct::<[u64; 2]>(d) }, ), SYS_MKNS => format!( - "mkns({:?})", - validate_slice(b as *const [usize; 2], c) + "mkns({:p} len: {})", + // TODO: Print out all scheme names? + + // Simply printing out simply the pointers and lengths may not provide that much useful + // debugging information, so only print the raw args. + b as *const u8, + c, ), SYS_MPROTECT => format!( "mprotect({:#X}, {}, {:?})", @@ -218,7 +240,7 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - ), SYS_NANOSLEEP => format!( "nanosleep({:?}, ({}, {}))", - validate_slice(b as *const TimeSpec, 1), + unsafe { read_struct::(b) }, c, d ), @@ -241,17 +263,13 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - c, PhysmapFlags::from_bits(d) ), - SYS_PHYSUNMAP => format!( - "physunmap({:#X})", - b - ), SYS_VIRTTOPHYS => format!( "virttophys({:#X})", b ), SYS_PIPE2 => format!( "pipe2({:?}, {})", - validate_slice_mut(b as *mut usize, 2), + unsafe { read_struct::<[usize; 2]>(b) }, c ), SYS_SETREGID => format!( diff --git a/src/syscall/driver.rs b/src/syscall/driver.rs index 170580bed6..6a829f912c 100644 --- a/src/syscall/driver.rs +++ b/src/syscall/driver.rs @@ -1,8 +1,8 @@ use crate::interrupt::InterruptStack; use crate::memory::{allocate_frames_complex, deallocate_frames, Frame, PAGE_SIZE}; -use crate::paging::{PageFlags, PhysicalAddress, VirtualAddress, mapper::PageFlushAll}; +use crate::paging::{PageFlags, PhysicalAddress, VirtualAddress}; use crate::context; -use crate::context::memory::{Grant, Region}; +use crate::context::memory::Grant; use crate::syscall::error::{Error, EFAULT, EINVAL, ENOMEM, EPERM, ESRCH, Result}; use crate::syscall::flag::{PhysallocFlags, PartialAllocStrategy, PhysmapFlags, PHYSMAP_WRITE, PHYSMAP_WRITE_COMBINE, PHYSMAP_NO_CACHE}; @@ -11,6 +11,8 @@ use crate::paging::entry::EntryFlags; use alloc::sync::Arc; +use super::usercopy::UserSliceRw; + fn enforce_root() -> Result<()> { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -53,17 +55,17 @@ pub fn iopl(level: usize, stack: &mut InterruptStack) -> Result { Ok(0) } -pub fn inner_physalloc(size: usize, flags: PhysallocFlags, strategy: Option, min: usize) -> Result<(usize, usize)> { +pub fn inner_physalloc(size: usize, flags: PhysallocFlags, strategy: Option, _min: usize) -> Result<(usize, usize)> { if flags.contains(PhysallocFlags::SPACE_32 | PhysallocFlags::SPACE_64) { return Err(Error::new(EINVAL)); } - allocate_frames_complex((size + 4095) / 4096, flags, strategy, (min + 4095) / 4096).ok_or(Error::new(ENOMEM)).map(|(frame, count)| (frame.start_address().data(), count * 4096)) + allocate_frames_complex(size.div_ceil(PAGE_SIZE), flags, strategy, size.div_ceil(PAGE_SIZE)).ok_or(Error::new(ENOMEM)).map(|(frame, count)| (frame.start_address().data(), count * PAGE_SIZE)) } pub fn physalloc(size: usize) -> Result { enforce_root()?; inner_physalloc(size, PhysallocFlags::SPACE_64, None, size).map(|(base, _)| base) } -pub fn physalloc3(size: usize, flags_raw: usize, min: &mut usize) -> Result { +pub fn physalloc3(size: usize, flags_raw: usize, min_inout_usize: UserSliceRw) -> Result { enforce_root()?; let flags = PhysallocFlags::from_bits(flags_raw & !syscall::PARTIAL_ALLOC_STRATEGY_MASK).ok_or(Error::new(EINVAL))?; let strategy = if flags.contains(PhysallocFlags::PARTIAL_ALLOC) { @@ -71,15 +73,18 @@ pub fn physalloc3(size: usize, flags_raw: usize, min: &mut usize) -> Result Result { - deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), (size + 4095)/4096); + deallocate_frames(Frame::containing_address(PhysicalAddress::new(physical_address)), size.div_ceil(PAGE_SIZE)); - //TODO: Check that no double free occured + //TODO: Check that no double free occured. Perhaps by making userspace Ok(0) } pub fn physfree(physical_address: usize, size: usize) -> Result { @@ -88,9 +93,6 @@ pub fn physfree(physical_address: usize, size: usize) -> Result { } //TODO: verify exlusive access to physical memory -// TODO: Replace this completely with something such as `memory:physical`. Mmapping at offset -// `physaddr` to `address` (optional) will map that physical address. We would have to find out -// some way to pass flags such as WRITE_COMBINE/NO_CACHE however. pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result { // TODO: Check physical_address against MAXPHYADDR. @@ -102,7 +104,7 @@ pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags) if size % PAGE_SIZE != 0 { log::warn!("physmap size {} is not multiple of PAGE_SIZE {}", size, PAGE_SIZE); } - let pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + let pages = size.div_ceil(PAGE_SIZE); let addr_space = Arc::clone(context::current()?.read().addr_space()?); let mut addr_space = addr_space.write(); @@ -131,33 +133,13 @@ pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags) }).map(|page| page.start_address().data()) } -// TODO: Remove this syscall, funmap makes it redundant. +// TODO: Replace physmap with e.g. fmap fd=`memory:physical@wc` or `memory:physical@uncacheable`, +// or represent memory types as fmap flags. pub fn physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result { enforce_root()?; inner_physmap(physical_address, size, flags) } -pub fn inner_physunmap(virtual_address: usize) -> Result { - if virtual_address == 0 { - Ok(0) - } else { - let addr_space = Arc::clone(context::current()?.read().addr_space()?); - let mut addr_space = addr_space.write(); - - if let Some(region) = addr_space.grants.contains(VirtualAddress::new(virtual_address)).map(Region::from) { - - addr_space.grants.take(®ion).unwrap().unmap(&mut addr_space.table.utable, PageFlushAll::new()); - return Ok(0); - } - - Err(Error::new(EFAULT)) - } -} -pub fn physunmap(virtual_address: usize) -> Result { - enforce_root()?; - inner_physunmap(virtual_address) -} - pub fn virttophys(virtual_address: usize) -> Result { enforce_root()?; diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index ca532a3bff..2661ef5e8b 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -1,19 +1,19 @@ //! Filesystem syscalls use alloc::sync::Arc; -use syscall::CallerCtx; -use core::str; use spin::RwLock; use crate::context::file::{FileDescriptor, FileDescription}; use crate::context; use crate::memory::PAGE_SIZE; -use crate::scheme::{self, FileHandle, OpenResult, current_caller_ctx}; -use crate::syscall::data::{Packet, Stat}; +use crate::scheme::{self, FileHandle, OpenResult, current_caller_ctx, KernelScheme, SchemeId}; +use crate::syscall::data::Stat; use crate::syscall::error::*; use crate::syscall::flag::*; +use crate::syscall::scheme::CallerCtx; +use super::usercopy::{UserSlice, UserSliceWo, UserSliceRo}; -pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result { +/*pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result { let (file, pid, uid, gid) = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -42,24 +42,49 @@ pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result { scheme.handle(&mut packet); Error::demux(packet.a) -} +}*/ -pub fn file_op_slice(a: usize, fd: FileHandle, slice: &[u8]) -> Result { - file_op(a, fd, slice.as_ptr() as usize, slice.len()) +pub fn file_op_generic(fd: FileHandle, op: impl FnOnce(&dyn KernelScheme, &CallerCtx, usize) -> Result) -> Result { + file_op_generic_ext(fd, |s, _, ctx, no| op(s, ctx, no)) } +pub fn file_op_generic_ext(fd: FileHandle, op: impl FnOnce(&dyn KernelScheme, SchemeId, &CallerCtx, usize) -> Result) -> Result { + let (ctx, file) = match context::current()?.read() { + ref context => (CallerCtx { pid: context.id.into(), uid: context.euid, gid: context.egid }, context.get_file(fd).ok_or(Error::new(EBADF))?), + }; + let FileDescription { scheme: scheme_id, number, .. } = *file.description.read(); -pub fn file_op_mut_slice(a: usize, fd: FileHandle, slice: &mut [u8]) -> Result { - file_op(a, fd, slice.as_mut_ptr() as usize, slice.len()) + let scheme = Arc::clone(scheme::schemes().get(scheme_id).ok_or(Error::new(EBADF))?); + + op(&*scheme, scheme_id, &ctx, number) } +pub fn copy_path_to_buf(raw_path: UserSliceRo, max_len: usize) -> Result { + let mut path_buf = vec! [0_u8; max_len]; + if raw_path.len() > path_buf.len() { + return Err(Error::new(ENAMETOOLONG)); + } + let path_len = raw_path.copy_common_bytes_to_slice(&mut path_buf)?; + path_buf.truncate(path_len); + alloc::string::String::from_utf8(path_buf).map_err(|_| Error::new(EINVAL)) + //core::str::from_utf8(&path_buf[..path_len]).map_err(|_| Error::new(EINVAL)) +} +// TODO: Define elsewhere +const PATH_MAX: usize = PAGE_SIZE; /// Open syscall -pub fn open(path: &str, flags: usize) -> Result { +pub fn open(raw_path: UserSliceRo, flags: usize) -> Result { let (pid, uid, gid, scheme_ns, umask) = match context::current()?.read() { ref context => (context.id.into(), context.euid, context.egid, context.ens, context.umask), }; let flags = (flags & (!0o777)) | ((flags & 0o777) & (!(umask & 0o777))); + // TODO: BorrowedHtBuf! + + /* + let mut path_buf = BorrowedHtBuf::head()?; + let path = path_buf.use_for_string(raw_path)?; + */ + let path = copy_path_to_buf(raw_path, PATH_MAX)?; let mut parts = path.splitn(2, ':'); let scheme_name = parts.next().ok_or(Error::new(EINVAL))?; @@ -82,6 +107,7 @@ pub fn open(path: &str, flags: usize) -> Result { OpenResult::External(desc) => desc, } }; + //drop(path_buf); context::current()?.read().add_file(FileDescriptor { description, @@ -89,11 +115,7 @@ pub fn open(path: &str, flags: usize) -> Result { }).ok_or(Error::new(EMFILE)) } -pub fn pipe2(fds: &mut [usize], flags: usize) -> Result { - if fds.len() < 2 { - return Err(Error::new(EINVAL)); - } - +pub fn pipe2(fds: UserSliceWo, flags: usize) -> Result<()> { let scheme_id = crate::scheme::pipe::pipe_scheme_id(); let (read_id, write_id) = crate::scheme::pipe::pipe(flags)?; @@ -122,21 +144,23 @@ pub fn pipe2(fds: &mut [usize], flags: usize) -> Result { cloexec: flags & O_CLOEXEC == O_CLOEXEC, }).ok_or(Error::new(EMFILE))?; - fds[0] = read_fd.into(); - fds[1] = write_fd.into(); - - Ok(0) + let (read_outptr, write_outptr) = fds.split_at(core::mem::size_of::()).ok_or(Error::new(EINVAL))?; + read_outptr.write_usize(read_fd.into())?; + write_outptr.write_usize(write_fd.into()) } /// rmdir syscall -pub fn rmdir(path: &str) -> Result { - let (uid, gid, scheme_ns) = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - (context.euid, context.egid, context.ens) +pub fn rmdir(raw_path: UserSliceRo) -> Result { + let (uid, gid, scheme_ns) = match context::current()?.read() { + ref context => (context.euid, context.egid, context.ens), }; + /* + let mut path_buf = BorrowedHtBuf::head()?; + let path = path_buf.use_for_string(raw_path)?; + */ + let path = copy_path_to_buf(raw_path, PATH_MAX)?; + let mut parts = path.splitn(2, ':'); let scheme_name = parts.next().ok_or(Error::new(EINVAL))?; let reference = parts.next().unwrap_or(""); @@ -150,13 +174,15 @@ pub fn rmdir(path: &str) -> Result { } /// Unlink syscall -pub fn unlink(path: &str) -> Result { - let (uid, gid, scheme_ns) = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - (context.euid, context.egid, context.ens) +pub fn unlink(raw_path: UserSliceRo) -> Result { + let (uid, gid, scheme_ns) = match context::current()?.read() { + ref context => (context.euid, context.egid, context.ens), }; + /* + let mut path_buf = BorrowedHtBuf::head()?; + let path = path_buf.use_for_string(raw_path)?; + */ + let path = copy_path_to_buf(raw_path, PATH_MAX)?; let mut parts = path.splitn(2, ':'); let scheme_name = parts.next().ok_or(Error::new(EINVAL))?; @@ -182,15 +208,11 @@ pub fn close(fd: FileHandle) -> Result { file.close() } -fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result { - let file = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - context.get_file(fd).ok_or(Error::new(EBADF))? - }; +fn duplicate_file(fd: FileHandle, user_buf: UserSliceRo) -> Result { + let file = context::current()?.read() + .get_file(fd).ok_or(Error::new(EBADF))?; - if buf.is_empty() { + if user_buf.is_empty() { Ok(FileDescriptor { description: Arc::clone(&file.description), cloexec: false, @@ -204,7 +226,8 @@ fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result { let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?; Arc::clone(scheme) }; - match scheme.kdup(description.number, buf, current_caller_ctx()?)? { + + match scheme.kdup(description.number, user_buf, current_caller_ctx()?)? { OpenResult::SchemeLocal(number) => Arc::new(RwLock::new(FileDescription { namespace: description.namespace, scheme: description.scheme, @@ -223,18 +246,14 @@ fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result { } /// Duplicate file descriptor -pub fn dup(fd: FileHandle, buf: &[u8]) -> Result { +pub fn dup(fd: FileHandle, buf: UserSliceRo) -> Result { let new_file = duplicate_file(fd, buf)?; - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - - context.add_file(new_file).ok_or(Error::new(EMFILE)) + context::current()?.read().add_file(new_file).ok_or(Error::new(EMFILE)) } /// Duplicate file descriptor, replacing another -pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: &[u8]) -> Result { +pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: UserSliceRo) -> Result { if fd == new_fd { Ok(new_fd) } else { @@ -274,7 +293,7 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result { { if cmd == F_DUPFD { // Not in match because 'files' cannot be locked - let new_file = duplicate_file(fd, &[])?; + let new_file = duplicate_file(fd, UserSlice::empty())?; let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -321,11 +340,17 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result { } } -pub fn frename(fd: FileHandle, path: &str) -> Result { +pub fn frename(fd: FileHandle, raw_path: UserSliceRo) -> Result { let (file, uid, gid, scheme_ns) = match context::current()?.read() { ref context => (context.get_file(fd).ok_or(Error::new(EBADF))?, context.euid, context.egid, context.ens), }; + /* + let mut path_buf = BorrowedHtBuf::head()?; + let path = path_buf.use_for_string(raw_path)?; + */ + let path = copy_path_to_buf(raw_path, PATH_MAX)?; + let mut parts = path.splitn(2, ':'); let scheme_name = parts.next().ok_or(Error::new(ENOENT))?; let reference = parts.next().unwrap_or(""); @@ -346,33 +371,27 @@ pub fn frename(fd: FileHandle, path: &str) -> Result { } /// File status -pub fn fstat(fd: FileHandle, stat: &mut Stat) -> Result { - let file = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - context.get_file(fd).ok_or(Error::new(EBADF))? - }; +pub fn fstat(fd: FileHandle, user_buf: UserSliceWo) -> Result { + file_op_generic_ext(fd, |scheme, scheme_id, _, number| { + scheme.kfstat(number, user_buf)?; - let description = file.description.read(); + // TODO: Ensure only the kernel can access the stat when st_dev is set, or use another API + // for retrieving the scheme ID from a file descriptor. + // TODO: Less hacky method. + let st_dev = scheme_id.into().try_into().map_err(|_| Error::new(EOVERFLOW))?; + user_buf.advance(memoffset::offset_of!(Stat, st_dev)).and_then(|b| b.limit(8)).ok_or(Error::new(EIO))?.copy_from_slice(&u64::to_ne_bytes(st_dev))?; - let scheme = { - let schemes = scheme::schemes(); - let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?; - Arc::clone(scheme) - }; - // Fill in scheme number as device number - stat.st_dev = description.scheme.into() as u64; - scheme.fstat(description.number, stat) + Ok(0) + }) } pub fn funmap(virtual_address: usize, length: usize) -> Result { - let length_aligned = ((length + (PAGE_SIZE - 1))/PAGE_SIZE) * PAGE_SIZE; + let length_aligned = length.next_multiple_of(PAGE_SIZE); if length != length_aligned { log::warn!("funmap passed length {:#x} instead of {:#x}", length, length_aligned); } - let (page, page_count) = crate::syscall::validate::validate_region(virtual_address, length_aligned)?; + let (page, page_count) = crate::syscall::validate_region(virtual_address, length_aligned)?; let addr_space = Arc::clone(context::current()?.read().addr_space()?); addr_space.write().munmap(page, page_count); diff --git a/src/syscall/futex.rs b/src/syscall/futex.rs index 8f7ca2fc8f..d6c903a771 100644 --- a/src/syscall/futex.rs +++ b/src/syscall/futex.rs @@ -4,6 +4,7 @@ //! For more information about futexes, please read [this](https://eli.thegreenplace.net/2018/basics-of-futexes/) blog post, and the [futex(2)](http://man7.org/linux/man-pages/man2/futex.2.html) man page use alloc::collections::VecDeque; use alloc::sync::Arc; +use rmm::Arch; use core::intrinsics; use spin::RwLock; @@ -15,7 +16,8 @@ use crate::time; use crate::syscall::data::TimeSpec; use crate::syscall::error::{Error, Result, EAGAIN, EFAULT, EINVAL, ESRCH}; use crate::syscall::flag::{FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAIT64, FUTEX_WAKE}; -use crate::syscall::validate::validate_array; + +use super::usercopy::UserSlice; type FutexList = VecDeque; @@ -43,23 +45,21 @@ fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Optio } pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> Result { - let addr_space = Arc::clone(context::current()?.read().addr_space()?); + let addr_space_lock = Arc::clone(context::current()?.read().addr_space()?); + + // Keep the address space locked so we can safely read from the physical address. Unlock it + // before context switching. + let addr_space_guard = addr_space_lock.read(); let target_physaddr = - validate_and_translate_virt(&*addr_space.read(), VirtualAddress::new(addr)) + validate_and_translate_virt(&*addr_space_guard, VirtualAddress::new(addr)) .ok_or(Error::new(EFAULT))?; match op { // TODO: FUTEX_WAIT_MULTIPLE? FUTEX_WAIT | FUTEX_WAIT64 => { - let timeout_ptr = val2 as *const TimeSpec; - - let timeout_opt = if timeout_ptr.is_null() { - None - } else { - let [timeout] = unsafe { *validate_array(timeout_ptr)? }; - Some(timeout) - }; + let timeout_opt = UserSlice::ro(val2, core::mem::size_of::())?.none_if_null() + .map(|buf| unsafe { buf.read_exact::() }).transpose()?; { let mut futexes = FUTEXES.write(); @@ -72,9 +72,14 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R if addr % 4 != 0 { return Err(Error::new(EINVAL)); } + + // On systems where virtual memory is not abundant, we might instead add an + // atomic usercopy function. + let accessible_addr = unsafe { crate::paging::RmmA::phys_to_virt(target_physaddr) }.data(); + ( u64::from(unsafe { - intrinsics::atomic_load_seqcst::(addr as *const u32) + intrinsics::atomic_load_seqcst::(accessible_addr as *const u32) }), u64::from(val as u32), ) @@ -112,6 +117,8 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R }); } + drop(addr_space_guard); + unsafe { context::switch(); } @@ -157,9 +164,11 @@ pub fn futex(addr: usize, op: usize, val: usize, val2: usize, addr2: usize) -> R } FUTEX_REQUEUE => { let addr2_physaddr = - validate_and_translate_virt(&*addr_space.read(), VirtualAddress::new(addr2)) + validate_and_translate_virt(&*addr_space_guard, VirtualAddress::new(addr2)) .ok_or(Error::new(EFAULT))?; + drop(addr_space_guard); + let mut woken = 0; let mut requeued = 0; diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index eb6cf362ce..91597a677d 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -4,6 +4,8 @@ extern crate syscall; +use syscall::{EventFlags, EOVERFLOW}; + pub use self::syscall::{ FloatRegisters, IntRegisters, @@ -23,11 +25,11 @@ pub use self::futex::futex; pub use self::privilege::*; pub use self::process::*; pub use self::time::*; -pub use self::validate::*; +pub use self::usercopy::validate_region; use self::scheme::Scheme as _; -use self::data::{Map, SigAction, Stat, TimeSpec}; +use self::data::{Map, SigAction, TimeSpec}; use self::error::{Error, Result, ENOSYS}; use self::flag::{MapFlags, PhysmapFlags, WaitFlags}; use self::number::*; @@ -35,6 +37,7 @@ use self::number::*; use crate::context::ContextId; use crate::interrupt::InterruptStack; use crate::scheme::{FileHandle, SchemeNamespace, memory::MemoryScheme}; +use crate::syscall::usercopy::UserSlice; /// Debug pub mod debug; @@ -57,8 +60,8 @@ pub mod process; /// Time syscalls pub mod time; -/// Validate input -pub mod validate; +/// Safely copying memory between user and kernel memory +pub mod usercopy; /// This function is the syscall handler of the kernel, it is composed of an inner function that returns a `Result`. After the inner function runs, the syscall /// function calls [`Error::mux`] on it. @@ -71,43 +74,61 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack let fd = FileHandle::from(b); match a & SYS_ARG { SYS_ARG_SLICE => match a { - SYS_FMAP if b == !0 => { - MemoryScheme.fmap(!0, unsafe { validate_ref(c as *const Map, d)? }) + SYS_WRITE => file_op_generic(fd, |scheme, _, number| scheme.kwrite(number, UserSlice::ro(c, d)?)), + SYS_FMAP => { + let map = unsafe { UserSlice::ro(c, d)?.read_exact::()? }; + if b == !0 { + MemoryScheme.fmap(!0, &map) + } else { + file_op_generic(fd, |scheme, _, number| scheme.fmap(number, &map)) + } }, - _ => file_op_slice(a, fd, validate_slice(c as *const u8, d)?), + // SYS_FMAP_OLD is ignored + SYS_FUTIMENS => file_op_generic(fd, |scheme, _, number| scheme.kfutimens(number, UserSlice::ro(c, d)?)), + + _ => return Err(Error::new(ENOSYS)), } SYS_ARG_MSLICE => match a { - SYS_FSTAT => fstat(fd, unsafe { validate_ref_mut(c as *mut Stat, d)? }), - _ => file_op_mut_slice(a, fd, validate_slice_mut(c as *mut u8, d)?), + SYS_READ => file_op_generic(fd, |scheme, _, number| scheme.kread(number, UserSlice::wo(c, d)?)), + SYS_FPATH => file_op_generic(fd, |scheme, _, number| scheme.kfpath(number, UserSlice::wo(c, d)?)), + SYS_FSTAT => fstat(fd, UserSlice::wo(c, d)?), + SYS_FSTATVFS => file_op_generic(fd, |scheme, _, number| scheme.kfstatvfs(number, UserSlice::wo(c, d)?)), + + _ => return Err(Error::new(ENOSYS)), }, _ => match a { - SYS_CLOSE => close(fd), - SYS_DUP => dup(fd, validate_slice(c as *const u8, d)?).map(FileHandle::into), - SYS_DUP2 => dup2(fd, FileHandle::from(c), validate_slice(d as *const u8, e)?).map(FileHandle::into), + SYS_DUP => dup(fd, UserSlice::ro(c, d)?).map(FileHandle::into), + SYS_DUP2 => dup2(fd, FileHandle::from(c), UserSlice::ro(d, e)?).map(FileHandle::into), + SYS_LSEEK => file_op_generic(fd, |scheme, _, number| Ok(scheme.seek(number, c as isize, d)? as usize)), + SYS_FCHMOD => file_op_generic(fd, |scheme, _, number| scheme.fchmod(number, c as u16)), + SYS_FCHOWN => file_op_generic(fd, |scheme, _, number| scheme.fchown(number, c as u32, d as u32)), SYS_FCNTL => fcntl(fd, c, d), - SYS_FRENAME => frename(fd, validate_str(c as *const u8, d)?), + SYS_FEVENT => file_op_generic(fd, |scheme, _, number| Ok(scheme.fevent(number, EventFlags::from_bits_truncate(c))?.bits())), + SYS_FRENAME => frename(fd, UserSlice::ro(c, d)?), SYS_FUNMAP => funmap(b, c), - _ => file_op(a, fd, c, d) + + SYS_FSYNC => file_op_generic(fd, |scheme, _, number| scheme.fsync(number)), + SYS_FTRUNCATE => file_op_generic(fd, |scheme, _, number| scheme.ftruncate(number, c)), + + SYS_CLOSE => close(fd), + + _ => return Err(Error::new(ENOSYS)), } } }, SYS_CLASS_PATH => match a { - SYS_OPEN => open(validate_str(b as *const u8, c)?, d).map(FileHandle::into), - SYS_RMDIR => rmdir(validate_str(b as *const u8, c)?), - SYS_UNLINK => unlink(validate_str(b as *const u8, c)?), + SYS_OPEN => open(UserSlice::ro(b, c)?, d).map(FileHandle::into), + SYS_RMDIR => rmdir(UserSlice::ro(b, c)?), + SYS_UNLINK => unlink(UserSlice::ro(b, c)?), _ => Err(Error::new(ENOSYS)) }, _ => match a { - SYS_YIELD => sched_yield(), + SYS_YIELD => sched_yield().map(|()| 0), SYS_NANOSLEEP => nanosleep( - validate_slice(b as *const TimeSpec, 1).map(|req| &req[0])?, - if c == 0 { - None - } else { - Some(validate_slice_mut(c as *mut TimeSpec, 1).map(|rem| &mut rem[0])?) - } - ), - SYS_CLOCK_GETTIME => clock_gettime(b, validate_slice_mut(c as *mut TimeSpec, 1).map(|time| &mut time[0])?), + UserSlice::ro(b, core::mem::size_of::())?, + UserSlice::wo(c, core::mem::size_of::())?.none_if_null(), + ).map(|()| 0), + SYS_CLOCK_GETTIME => clock_gettime(b, UserSlice::wo(c, core::mem::size_of::())?).map(|()| 0), SYS_FUTEX => futex(b, c, d, e, f), SYS_GETPID => getpid().map(ContextId::into), SYS_GETPGID => getpgid(ContextId::from(b)).map(ContextId::into), @@ -115,7 +136,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack SYS_EXIT => exit((b & 0xFF) << 8), SYS_KILL => kill(ContextId::from(b), c), - SYS_WAITPID => waitpid(ContextId::from(b), c, WaitFlags::from_bits_truncate(d)).map(ContextId::into), + SYS_WAITPID => waitpid(ContextId::from(b), if c == 0 { None } else { Some(UserSlice::wo(c, core::mem::size_of::())?) }, WaitFlags::from_bits_truncate(d)).map(ContextId::into), SYS_IOPL => iopl(b, stack), SYS_GETEGID => getegid(), SYS_GETENS => getens(), @@ -124,45 +145,28 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack SYS_GETNS => getns(), SYS_GETUID => getuid(), SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d)), - SYS_MKNS => mkns(validate_slice(b as *const [usize; 2], c)?), + SYS_MKNS => mkns(UserSlice::ro(b, c.checked_mul(core::mem::size_of::<[usize; 2]>()).ok_or(Error::new(EOVERFLOW))?)?), SYS_SETPGID => setpgid(ContextId::from(b), ContextId::from(c)), SYS_SETREUID => setreuid(b as u32, c as u32), SYS_SETRENS => setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)), SYS_SETREGID => setregid(b as u32, c as u32), SYS_SIGACTION => sigaction( b, - if c == 0 { - None - } else { - Some(validate_slice(c as *const SigAction, 1).map(|act| &act[0])?) - }, - if d == 0 { - None - } else { - Some(validate_slice_mut(d as *mut SigAction, 1).map(|oldact| &mut oldact[0])?) - }, - e - ), + UserSlice::ro(c, core::mem::size_of::())?.none_if_null(), + UserSlice::wo(d, core::mem::size_of::())?.none_if_null(), + e, + ).map(|()| 0), SYS_SIGPROCMASK => sigprocmask( b, - if c == 0 { - None - } else { - Some(validate_slice(c as *const [u64; 2], 1).map(|s| &s[0])?) - }, - if d == 0 { - None - } else { - Some(validate_slice_mut(d as *mut [u64; 2], 1).map(|s| &mut s[0])?) - } - ), + UserSlice::ro(c, 16)?.none_if_null(), + UserSlice::wo(d, 16)?.none_if_null(), + ).map(|()| 0), SYS_SIGRETURN => sigreturn(), - SYS_PIPE2 => pipe2(validate_slice_mut(b as *mut usize, 2)?, c), + SYS_PIPE2 => pipe2(UserSlice::wo(b, 2 * core::mem::size_of::())?, c).map(|()| 0), SYS_PHYSALLOC => physalloc(b), - SYS_PHYSALLOC3 => physalloc3(b, c, &mut validate_slice_mut(d as *mut usize, 1)?[0]), + SYS_PHYSALLOC3 => physalloc3(b, c, UserSlice::rw(d, core::mem::size_of::())?), SYS_PHYSFREE => physfree(b, c), SYS_PHYSMAP => physmap(b, c, PhysmapFlags::from_bits_truncate(d)), - SYS_PHYSUNMAP => physunmap(b), SYS_UMASK => umask(b), SYS_VIRTTOPHYS => virttophys(b), _ => Err(Error::new(ENOSYS)) @@ -170,13 +174,13 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack } } - /* - let debug = { + let mut debug = false; + + debug = debug && { let contexts = crate::context::contexts(); if let Some(context_lock) = contexts.current() { let context = context_lock.read(); - let name = context.name.read(); - if name.contains("bootstrap") { + if context.name.contains("bootstrap") { if a == SYS_CLOCK_GETTIME || a == SYS_YIELD { false } else if (a == SYS_WRITE || a == SYS_FSYNC) && (b == 1 || b == 2) { @@ -196,16 +200,18 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack let contexts = crate::context::contexts(); if let Some(context_lock) = contexts.current() { let context = context_lock.read(); - print!("{} ({}): ", *context.name.read(), context.id.into()); + print!("{} ({}): ", context.name, context.id.into()); } - println!("{}", debug::format_call(a, b, c, d, e, f)); + // Do format_call outside print! so possible exception handlers cannot reentrantly + // deadlock. + let string = debug::format_call(a, b, c, d, e, f); + println!("{}", string); crate::time::monotonic() } else { 0 }; - */ // The next lines set the current syscall in the context struct, then once the inner() function // completes, we set the current syscall to none. @@ -230,17 +236,19 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack } } - /* if debug { let debug_duration = crate::time::monotonic() - debug_start; let contexts = crate::context::contexts(); if let Some(context_lock) = contexts.current() { let context = context_lock.read(); - print!("{} ({}): ", *context.name.read(), context.id.into()); + print!("{} ({}): ", context.name, context.id.into()); } - print!("{} = ", debug::format_call(a, b, c, d, e, f)); + // Do format_call outside print! so possible exception handlers cannot reentrantly + // deadlock. + let string = debug::format_call(a, b, c, d, e, f); + print!("{} = ", string); match result { Ok(ref ok) => { @@ -253,7 +261,6 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack println!(" in {} ns", debug_duration); } - */ // errormux turns Result into -errno Error::mux(result) diff --git a/src/syscall/privilege.rs b/src/syscall/privilege.rs index bd072782fc..6165171d28 100644 --- a/src/syscall/privilege.rs +++ b/src/syscall/privilege.rs @@ -3,7 +3,9 @@ use alloc::vec::Vec; use crate::context; use crate::scheme::{self, SchemeNamespace}; use crate::syscall::error::*; -use crate::syscall::validate::validate_str; + +use super::copy_path_to_buf; +use super::usercopy::{UserSlice, UserSliceRo}; pub fn getegid() -> Result { let contexts = context::contexts(); @@ -47,25 +49,35 @@ pub fn getuid() -> Result { Ok(context.ruid as usize) } -pub fn mkns(name_ptrs: &[[usize; 2]]) -> Result { - let mut names = Vec::new(); - for name_ptr in name_ptrs { - names.push(validate_str(name_ptr[0] as *const u8, name_ptr[1])?); - } - - let (uid, from) = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - (context.euid, context.ens) +pub fn mkns(mut user_buf: UserSliceRo) -> Result { + let (uid, from) = match context::current()?.read() { + ref context => (context.euid, context.ens), }; - if uid == 0 { - let to = scheme::schemes_mut().make_ns(from, &names)?; - Ok(to.into()) - } else { - Err(Error::new(EACCES)) + // TODO: Lift this restriction later? + if uid != 0 { + return Err(Error::new(EACCES)); } + + let mut names = Vec::with_capacity(user_buf.len() / core::mem::size_of::<[usize; 2]>()); + + while let Some((current_name_ptr_buf, next_part)) = user_buf.split_at(core::mem::size_of::<[usize; 2]>()) { + let mut iter = current_name_ptr_buf.usizes(); + let ptr = iter.next().ok_or(Error::new(EINVAL))??; + let len = iter.next().ok_or(Error::new(EINVAL))??; + + let raw_path = UserSlice::new(ptr, len)?; + + // TODO: Max scheme size limit? + let max_len = 256; + + names.push(copy_path_to_buf(raw_path, max_len)?.into_boxed_str()); + + user_buf = next_part; + } + + let to = scheme::schemes_mut().make_ns(from, names)?; + Ok(to.into()) } pub fn setregid(rgid: u32, egid: u32) -> Result { diff --git a/src/syscall/process.rs b/src/syscall/process.rs index dbd45254f1..9b5242aa76 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -21,7 +21,8 @@ use crate::syscall::flag::{wifcontinued, wifstopped, MapFlags, PTRACE_STOP_EXIT, SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK, SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED}; use crate::syscall::ptrace_event; -use crate::syscall::validate::validate_slice_mut; + +use super::usercopy::{UserSliceWo, UserSliceRo}; fn empty<'lock>(context_lock: &'lock RwLock, mut context: RwLockWriteGuard<'lock, Context>, reaping: bool) -> RwLockWriteGuard<'lock, Context> { // NOTE: If we do not replace the grants `Arc`, then a strange situation can appear where the @@ -323,7 +324,7 @@ pub fn setpgid(pid: ContextId, pgid: ContextId) -> Result { } } -pub fn sigaction(sig: usize, act_opt: Option<&SigAction>, oldact_opt: Option<&mut SigAction>, restorer: usize) -> Result { +pub fn sigaction(sig: usize, act_opt: Option, oldact_opt: Option, restorer: usize) -> Result<()> { if sig == 0 || sig > 0x7F { return Err(Error::new(EINVAL)); } @@ -333,47 +334,53 @@ pub fn sigaction(sig: usize, act_opt: Option<&SigAction>, oldact_opt: Option<&mu let mut actions = context.actions.write(); if let Some(oldact) = oldact_opt { - *oldact = actions[sig].0; + oldact.copy_exactly(&actions[sig].0)?; } if let Some(act) = act_opt { - actions[sig] = (*act, restorer); + actions[sig] = (unsafe { act.read_exact::()? }, restorer); } - Ok(0) + Ok(()) } -pub fn sigprocmask(how: usize, mask_opt: Option<&[u64; 2]>, oldmask_opt: Option<&mut [u64; 2]>) -> Result { +pub fn sigprocmask(how: usize, mask_opt: Option, oldmask_opt: Option) -> Result<()> { { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let mut context = context_lock.write(); + let [old_lo, old_hi] = context.sigmask; + if let Some(oldmask) = oldmask_opt { - *oldmask = context.sigmask; + // TODO: sigprocmask should be u64 + let (lo_dst, hi_dst) = oldmask.split_at(core::mem::size_of::()).ok_or(Error::new(EINVAL))?; + lo_dst.write_u64(old_lo)?; + hi_dst.write_u64(old_hi)?; } if let Some(mask) = mask_opt { - match how { + let (lo_src, hi_src) = mask.split_at(core::mem::size_of::()).ok_or(Error::new(EINVAL))?; + let lo_arg = lo_src.read_u64()?; + let hi_arg = hi_src.read_u64()?; + + context.sigmask = match how { SIG_BLOCK => { - context.sigmask[0] |= mask[0]; - context.sigmask[1] |= mask[1]; + [old_lo | lo_arg, old_hi | hi_arg] }, SIG_UNBLOCK => { - context.sigmask[0] &= !mask[0]; - context.sigmask[1] &= !mask[1]; + [old_lo & !lo_arg, old_hi & !hi_arg] }, SIG_SETMASK => { - context.sigmask[0] = mask[0]; - context.sigmask[1] = mask[1]; + [lo_arg, hi_arg] }, _ => { return Err(Error::new(EINVAL)); } - } + }; } } - Ok(0) + Ok(()) } pub fn sigreturn() -> Result { @@ -432,39 +439,30 @@ fn reap(pid: ContextId) -> Result { Ok(pid) } -pub fn waitpid(pid: ContextId, status_ptr: usize, flags: WaitFlags) -> Result { +pub fn waitpid(pid: ContextId, status_ptr: Option, flags: WaitFlags) -> Result { let (ppid, waitpid) = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); (context.id, Arc::clone(&context.waitpid)) }; + let write_status = |value| status_ptr.map(|ptr| ptr.write_usize(value)).unwrap_or(Ok(())); - let mut tmp = [0]; - let status_slice = if status_ptr != 0 { - validate_slice_mut(status_ptr as *mut usize, 1)? - } else { - &mut tmp - }; - - let mut grim_reaper = |w_pid: ContextId, status: usize| -> Option> { + let grim_reaper = |w_pid: ContextId, status: usize| -> Option> { if wifcontinued(status) { if flags & WCONTINUED == WCONTINUED { - status_slice[0] = status; - Some(Ok(w_pid)) + Some(write_status(status).map(|()| w_pid)) } else { None } } else if wifstopped(status) { if flags & WUNTRACED == WUNTRACED { - status_slice[0] = status; - Some(Ok(w_pid)) + Some(write_status(status).map(|()| w_pid)) } else { None } } else { - status_slice[0] = status; - Some(reap(w_pid)) + Some(write_status(status).and_then(|()| reap(w_pid))) } }; diff --git a/src/syscall/time.rs b/src/syscall/time.rs index 24eed8053a..25a001984a 100644 --- a/src/syscall/time.rs +++ b/src/syscall/time.rs @@ -4,20 +4,25 @@ use crate::syscall::data::TimeSpec; use crate::syscall::error::*; use crate::syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC}; -pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result { +use super::usercopy::{UserSliceRo, UserSliceWo}; + +pub fn clock_gettime(clock: usize, buf: UserSliceWo) -> Result<()> { let arch_time = match clock { CLOCK_REALTIME => time::realtime(), CLOCK_MONOTONIC => time::monotonic(), _ => return Err(Error::new(EINVAL)) }; - time.tv_sec = (arch_time / time::NANOS_PER_SEC) as i64; - time.tv_nsec = (arch_time % time::NANOS_PER_SEC) as i32; - Ok(0) + buf.copy_exactly(&TimeSpec { + tv_sec: (arch_time / time::NANOS_PER_SEC) as i64, + tv_nsec: (arch_time % time::NANOS_PER_SEC) as i32, + }) } /// Nanosleep will sleep by switching the current context -pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result { +pub fn nanosleep(req_buf: UserSliceRo, rem_buf_opt: Option) -> Result<()> { + let req = unsafe { req_buf.read_exact::()? }; + //start is a tuple of (seconds, nanoseconds) let start = time::monotonic(); let end = start + (req.tv_sec as u128 * time::NANOS_PER_SEC) + (req.tv_nsec as u128); @@ -45,23 +50,27 @@ pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result Result { +pub fn sched_yield() -> Result<()> { unsafe { context::switch(); } - Ok(0) + Ok(()) } diff --git a/src/syscall/usercopy.rs b/src/syscall/usercopy.rs new file mode 100644 index 0000000000..15daacaf91 --- /dev/null +++ b/src/syscall/usercopy.rs @@ -0,0 +1,195 @@ +use crate::paging::{Page, VirtualAddress}; +use crate::memory::PAGE_SIZE; + +use crate::arch::{arch_copy_to_user, arch_copy_from_user}; + +use crate::syscall::error::{Error, EINVAL, EFAULT, Result}; + +#[derive(Clone, Copy)] +pub struct UserSlice { + base: usize, + len: usize, +} +pub type UserSliceRo = UserSlice; +pub type UserSliceWo = UserSlice; +pub type UserSliceRw = UserSlice; + +impl UserSlice { + pub fn empty() -> Self { + Self { + base: 0, + len: 0, + } + } + pub fn len(&self) -> usize { + self.len + } + pub fn is_empty(&self) -> bool { + self.len == 0 + } + pub fn addr(&self) -> usize { + self.base + } + pub fn new(base: usize, len: usize) -> Result { + if base >= crate::USER_END_OFFSET || base.saturating_add(len) >= crate::USER_END_OFFSET { + return Err(Error::new(EFAULT)); + } + + Ok(Self { base, len }) + } + /// Split [0, end) into [0, idx) and [idx, end) + pub fn split_at(self, idx: usize) -> Option<(Self, Self)> { + if idx > self.len { + return None; + } + Some((Self { base: self.base, len: idx }, Self { base: self.base + idx, len: self.len - idx })) + } + pub fn advance(self, by: usize) -> Option { + Some(self.split_at(by)?.1) + } + pub fn limit(self, to: usize) -> Option { + Some(self.split_at(to)?.0) + } + pub fn none_if_null(self) -> Option { + if self.addr() == 0 { None } else { Some(self) } + } + /// Not unsafe, because user memory is not covered by the memory model that decides if + /// something is UB, but it can break logic invariants + pub fn reinterpret_unchecked(self) -> UserSlice { + UserSlice { + base: self.base, + len: self.len, + } + } + pub fn in_variable_chunks(self, chunk_size: usize) -> impl Iterator { + (0..self.len()).step_by(chunk_size).map(move |i| self.advance(i).expect("already limited by length, must succeed")) + } + pub fn in_exact_chunks(self, chunk_size: usize) -> impl Iterator { + (0..self.len().div_floor(chunk_size)) + .map(move |i| { + self.advance(i * chunk_size).expect("already limited by length, must succeed") + .limit(chunk_size).expect("length is aligned") + }) + } +} +impl UserSlice { + pub fn copy_to_slice(self, slice: &mut [u8]) -> Result<()> { + debug_assert!(is_kernel_mem(slice)); + + if self.len != slice.len() { + return Err(Error::new(EINVAL)); + } + + if unsafe { arch_copy_from_user(slice.as_mut_ptr() as usize, self.base, self.len) } == 0 { + Ok(()) + } else { + Err(Error::new(EFAULT)) + } + } + pub unsafe fn read_exact(self) -> Result { + let mut t: T = core::mem::zeroed(); + let slice = unsafe { core::slice::from_raw_parts_mut((&mut t as *mut T).cast::(), core::mem::size_of::()) }; + + self.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_to_slice(slice)?; + + Ok(t) + } + pub fn copy_common_bytes_to_slice(self, slice: &mut [u8]) -> Result { + let min = core::cmp::min(self.len(), slice.len()); + self.limit(min).expect("min(len, x) is always <= len").copy_to_slice(&mut slice[..min])?; + Ok(min) + } + // TODO: Merge int IO functions? + pub fn read_usize(self) -> Result { + let mut ret = 0_usize.to_ne_bytes(); + self.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_to_slice(&mut ret)?; + Ok(usize::from_ne_bytes(ret)) + } + pub fn read_u32(self) -> Result { + let mut ret = 0_u32.to_ne_bytes(); + self.limit(4).ok_or(Error::new(EINVAL))?.copy_to_slice(&mut ret)?; + Ok(u32::from_ne_bytes(ret)) + } + pub fn read_u64(self) -> Result { + let mut ret = 0_u64.to_ne_bytes(); + self.limit(8).ok_or(Error::new(EINVAL))?.copy_to_slice(&mut ret)?; + Ok(u64::from_ne_bytes(ret)) + } + pub fn usizes(self) -> impl Iterator> { + self.in_exact_chunks(core::mem::size_of::()).map(Self::read_usize) + } +} +impl UserSlice { + pub fn copy_from_slice(self, slice: &[u8]) -> Result<()> { + debug_assert!(is_kernel_mem(slice)); + + if self.len != slice.len() { + return Err(Error::new(EINVAL)); + } + + if unsafe { arch_copy_to_user(self.base, slice.as_ptr() as usize, self.len) } == 0 { + Ok(()) + } else { + Err(Error::new(EFAULT)) + } + } + pub fn copy_common_bytes_from_slice(self, slice: &[u8]) -> Result { + let min = core::cmp::min(self.len(), slice.len()); + self.limit(min).expect("min(len, x) is always <= len").copy_from_slice(&slice[..min])?; + Ok(min) + } + pub fn copy_exactly(self, slice: &[u8]) -> Result<()> { + self.limit(slice.len()).ok_or(Error::new(EINVAL))?.copy_from_slice(slice)?; + Ok(()) + } + pub fn write_usize(self, word: usize) -> Result<()> { + self.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_from_slice(&word.to_ne_bytes())?; + Ok(()) + } + pub fn write_u32(self, int: u32) -> Result<()> { + self.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_from_slice(&int.to_ne_bytes())?; + Ok(()) + } + pub fn write_u64(self, int: u64) -> Result<()> { + self.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_from_slice(&int.to_ne_bytes())?; + Ok(()) + } +} + +impl UserSliceRo { + pub fn ro(base: usize, size: usize) -> Result { + Self::new(base, size) + } +} +impl UserSliceWo { + pub fn wo(base: usize, size: usize) -> Result { + Self::new(base, size) + } +} +impl UserSliceRw { + pub fn rw(base: usize, size: usize) -> Result { + Self::new(base, size) + } +} + +fn is_kernel_mem(slice: &[u8]) -> bool { + (slice.as_ptr() as usize) >= crate::USER_END_OFFSET && (slice.as_ptr() as usize).checked_add(slice.len()).is_some() +} + +/// Convert `[addr, addr+size)` into `(page, page_count)`. +/// +/// This will fail if: +/// +/// - the base address is not page-aligned, +/// - the length is not page-aligned, +/// - the region is empty (EINVAL), or +/// - any byte in the region exceeds USER_END_OFFSET (EFAULT). +pub fn validate_region(address: usize, size: usize) -> Result<(Page, usize)> { + if address % PAGE_SIZE != 0 || size % PAGE_SIZE != 0 || size == 0 { + return Err(Error::new(EINVAL)); + } + if address.saturating_add(size) > crate::USER_END_OFFSET { + return Err(Error::new(EFAULT)); + } + Ok((Page::containing_address(VirtualAddress::new(address)), size / PAGE_SIZE)) +} diff --git a/src/syscall/validate.rs b/src/syscall/validate.rs deleted file mode 100644 index 51f7b5ef2d..0000000000 --- a/src/syscall/validate.rs +++ /dev/null @@ -1,121 +0,0 @@ -// TODO: Maybe stop handing out slices and instead use a wrapper type that supports copying etc. -// Invalid pages will cause page faults, which can be handled so that they are caught and EFAULT is -// returned. This will also make SMAP much, much, easier. c.f. Linux's copy_from_user, copy_to_user -// which are written in assembly and handle page faults. -use core::{mem, slice, str}; - -use crate::context; -use crate::memory::PAGE_SIZE; -use crate::paging::{Page, TableKind, VirtualAddress}; -use crate::syscall::error::*; - -use alloc::sync::Arc; - -fn validate(address: usize, size: usize, writable: bool) -> Result<()> { - if VirtualAddress::new(address.saturating_add(size)).kind() != TableKind::User { - return Err(Error::new(EFAULT)); - } - - let end_offset = size.checked_sub(1).ok_or(Error::new(EFAULT))?; - let end_address = address.checked_add(end_offset).ok_or(Error::new(EFAULT))?; - - let addr_space = Arc::clone(context::current()?.read().addr_space()?); - let addr_space = addr_space.read(); - - let start_page = Page::containing_address(VirtualAddress::new(address)); - let end_page = Page::containing_address(VirtualAddress::new(end_address)); - for page in Page::range_inclusive(start_page, end_page) { - if let Some((_, flags)) = addr_space.table.utable.translate(page.start_address()) { - if !flags.has_user() { - // println!("{:X}: Not usermode", page.start_address().data()); - return Err(Error::new(EFAULT)); - } - - if writable && !flags.has_write() { - // println!("{:X}: Not writable {}", page.start_address().data(), writable); - return Err(Error::new(EFAULT)); - } - } else { - // println!("{:X}: Not found", page.start_address().data()); - return Err(Error::new(EFAULT)); - } - } - - Ok(()) -} - -/// Convert a pointer and length to reference, if valid -pub unsafe fn validate_ref(ptr: *const T, size: usize) -> Result<&'static T> { - if size == mem::size_of::() { - validate(ptr as usize, mem::size_of::(), false)?; - Ok(&*ptr) - } else { - Err(Error::new(EINVAL)) - } -} - -/// Convert a pointer and length to mutable reference, if valid -pub unsafe fn validate_ref_mut(ptr: *mut T, size: usize) -> Result<&'static mut T> { - if size == mem::size_of::() { - validate(ptr as usize, mem::size_of::(), false)?; - Ok(&mut *ptr) - } else { - Err(Error::new(EINVAL)) - } -} - -/// Convert a pointer and length to slice, if valid -//TODO: Mark unsafe -pub fn validate_slice(ptr: *const T, len: usize) -> Result<&'static [T]> { - if len == 0 { - Ok(&[]) - } else { - validate(ptr as usize, len * mem::size_of::(), false)?; - Ok(unsafe { slice::from_raw_parts(ptr, len) }) - } -} -/// Convert a pointer with fixed static length to a reference to an array, if valid. -// TODO: This is probably also quite unsafe, mainly because we have no idea unless we do very -// careful checking, that this upholds the rules that LLVM relies with shared references, namely -// that the value cannot change by others. Atomic volatile. -pub unsafe fn validate_array<'a, T, const N: usize>(ptr: *const T) -> Result<&'a [T; N]> { - validate(ptr as usize, mem::size_of::() * N, false)?; - Ok(&*ptr.cast::<[T; N]>()) -} -pub unsafe fn validate_array_mut<'a, T, const N: usize>(ptr: *mut T) -> Result<&'a mut [T; N]> { - validate(ptr as usize, mem::size_of::() * N, true)?; - Ok(&mut *ptr.cast::<[T; N]>()) -} - -/// Convert a pointer and length to slice, if valid -// TODO: Mark unsafe -// -// FIXME: This is probably never ever safe, except under very special circumstances. Any &mut -// reference will allow LLVM to assume that nobody else will ever modify this value, which is -// certainly not the case for multithreaded userspace programs. Instead, we will want something -// like atomic volatile. -pub fn validate_slice_mut(ptr: *mut T, len: usize) -> Result<&'static mut [T]> { - if len == 0 { - Ok(&mut []) - } else { - validate(ptr as usize, len * mem::size_of::(), true)?; - Ok(unsafe { slice::from_raw_parts_mut(ptr, len) }) - } -} - -/// Convert a pointer and length to str, if valid -//TODO: Mark unsafe -pub fn validate_str(ptr: *const u8, len: usize) -> Result<&'static str> { - let slice = validate_slice(ptr, len)?; - str::from_utf8(slice).map_err(|_| Error::new(EINVAL)) -} - -pub fn validate_region(address: usize, size: usize) -> Result<(Page, usize)> { - if address % PAGE_SIZE != 0 || size % PAGE_SIZE != 0 || size == 0 { - return Err(Error::new(EINVAL)); - } - if address.saturating_add(size) > crate::USER_END_OFFSET { - return Err(Error::new(EFAULT)); - } - Ok((Page::containing_address(VirtualAddress::new(address)), size / PAGE_SIZE)) -} diff --git a/syscall b/syscall index 4d37b9fc1f..3b298a50a1 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 4d37b9fc1f6660057335d9b5ed7b0cef35ab78f9 +Subproject commit 3b298a50a158b7b252ece0cb0bf3de498b175b10