From ce50faf7ca621763164f87a6332c16f34ad27e9b Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 13 Sep 2016 20:06:39 -0600 Subject: [PATCH] Allow cloning of kernel threads. Userspace breaks potentially due to stack aliasing --- arch/x86_64/src/context.rs | 4 ++- arch/x86_64/src/interrupt/syscall.rs | 14 ++++++-- arch/x86_64/src/start.rs | 5 ++- kernel/context/mod.rs | 27 ++++++++++++--- kernel/syscall/mod.rs | 47 +++++++++++++------------- kernel/syscall/process.rs | 50 ++++++++++++++++++++++++++-- 6 files changed, 109 insertions(+), 38 deletions(-) diff --git a/arch/x86_64/src/context.rs b/arch/x86_64/src/context.rs index 4c9ba54..abe8945 100644 --- a/arch/x86_64/src/context.rs +++ b/arch/x86_64/src/context.rs @@ -6,7 +6,7 @@ use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; /// This must be done, as no locks can be held on the stack during switch pub static CONTEXT_SWITCH_LOCK: AtomicBool = ATOMIC_BOOL_INIT; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Context { /// Page table pointer cr3: usize, @@ -96,5 +96,7 @@ impl Context { // Unset global lock, set inside of kernel CONTEXT_SWITCH_LOCK.store(false, Ordering::SeqCst); + + asm!("xchg bx, bx" : : : : "intel", "volatile"); } } diff --git a/arch/x86_64/src/interrupt/syscall.rs b/arch/x86_64/src/interrupt/syscall.rs index 7133906..d97154e 100644 --- a/arch/x86_64/src/interrupt/syscall.rs +++ b/arch/x86_64/src/interrupt/syscall.rs @@ -3,7 +3,7 @@ pub unsafe extern fn syscall() { #[inline(never)] unsafe fn inner() { extern { - fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> usize; + fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize; } let mut a; @@ -13,10 +13,11 @@ pub unsafe extern fn syscall() { let d; let e; let f; - asm!("" : "={rax}"(a), "={rbx}"(b), "={rcx}"(c), "={rdx}"(d), "={rsi}"(e), "={rdi}"(f) + let stack; + asm!("" : "={rax}"(a), "={rbx}"(b), "={rcx}"(c), "={rdx}"(d), "={rsi}"(e), "={rdi}"(f), "={rbp}"(stack) : : : "intel", "volatile"); - a = syscall(a, b, c, d, e, f); + a = syscall(a, b, c, d, e, f, stack); } asm!("" : : "{rax}"(a) : : "intel", "volatile"); @@ -36,3 +37,10 @@ pub unsafe extern fn syscall() { iretq" : : : : "intel", "volatile"); } + +#[naked] +pub unsafe extern fn clone_ret() -> usize { + asm!("pop rbp" + : : : : "intel", "volatile"); + 0 +} diff --git a/arch/x86_64/src/start.rs b/arch/x86_64/src/start.rs index 11a1290..df30403 100644 --- a/arch/x86_64/src/start.rs +++ b/arch/x86_64/src/start.rs @@ -187,8 +187,7 @@ pub unsafe extern fn kstart_ap(cpu_id: usize, page_table: usize, stack_start: us pub unsafe fn usermode(ip: usize, sp: usize) -> ! { // Go to usermode - asm!("xchg bx, bx - mov ds, ax + asm!("mov ds, ax mov es, ax mov fs, ax mov gs, ax @@ -201,7 +200,7 @@ pub unsafe fn usermode(ip: usize, sp: usize) -> ! { : // No output because it never returns : "{rax}"(gdt::GDT_USER_DATA << 3 | 3), // Stack segment "{rbx}"(sp), // Stack pointer - "{rcx}"(3 << 12 | 1 << 9), // Flags - Set IOPL and interrupt enable flag + "{rcx}"(3 << 12/* | 1 << 9*/), // Flags - Set IOPL and interrupt enable flag "{rdx}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment "{rsi}"(ip) // IP : // No clobers because it never returns diff --git a/kernel/context/mod.rs b/kernel/context/mod.rs index a31d7b4..4b657da 100644 --- a/kernel/context/mod.rs +++ b/kernel/context/mod.rs @@ -141,11 +141,25 @@ pub unsafe fn switch() { let mut to_ptr = 0 as *mut Context; - for (_pid, context_lock) in contexts().map.iter() { - let mut context = context_lock.write(); - if ! context.running && ! context.blocked && ! context.exited { - to_ptr = context.deref_mut() as *mut Context; - break; + for (pid, context_lock) in contexts().map.iter() { + if *pid > (*from_ptr).id { + let mut context = context_lock.write(); + if ! context.running && ! context.blocked && ! context.exited { + to_ptr = context.deref_mut() as *mut Context; + break; + } + } + } + + if to_ptr as usize == 0 { + for (pid, context_lock) in contexts().map.iter() { + if *pid < (*from_ptr).id { + let mut context = context_lock.write(); + if ! context.running && ! context.blocked && ! context.exited { + to_ptr = context.deref_mut() as *mut Context; + break; + } + } } } @@ -159,6 +173,9 @@ pub unsafe fn switch() { (&mut *from_ptr).running = false; (&mut *to_ptr).running = true; + if let Some(ref stack) = (*to_ptr).kstack { + arch::gdt::TSS.rsp[0] = (stack.as_ptr() as usize + stack.len() - 256) as u64; + } CONTEXT_ID.store((&mut *to_ptr).id, Ordering::SeqCst); (&mut *from_ptr).arch.switch_to(&mut (&mut *to_ptr).arch); diff --git a/kernel/syscall/mod.rs b/kernel/syscall/mod.rs index 3fdb7a5..bd53ad4 100644 --- a/kernel/syscall/mod.rs +++ b/kernel/syscall/mod.rs @@ -105,32 +105,33 @@ pub fn convert_slice_mut(ptr: *mut T, len: usize) -> Result<&'static mut [T]> Ok(unsafe { slice::from_raw_parts_mut(ptr, len) }) } -pub fn handle(a: usize, b: usize, c: usize, d: usize, e: usize, _f: usize) -> Result { - match Call::from(a) { - Ok(call) => match call { - Call::Exit => exit(b), - Call::Read => read(b, convert_slice_mut(c as *mut u8, d)?), - Call::Write => write(b, convert_slice(c as *const u8, d)?), - Call::Open => open(convert_slice(b as *const u8, c)?, d), - Call::Close => close(b), - Call::Exec => exec(convert_slice(b as *const u8, c)?, convert_slice(d as *const [usize; 2], e)?), - Call::GetPid => getpid(), - Call::Dup => dup(b), - Call::Brk => brk(b), - Call::Iopl => iopl(b), - Call::Clone => clone(b), - Call::SchedYield => sched_yield() - }, - Err(err) => { - println!("Unknown syscall {}", a); - Err(err) +#[no_mangle] +pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize { + #[inline(always)] + fn inner(a: usize, b: usize, c: usize, d: usize, e: usize, _f: usize, stack: usize) -> Result { + match Call::from(a) { + Ok(call) => match call { + Call::Exit => exit(b), + Call::Read => read(b, convert_slice_mut(c as *mut u8, d)?), + Call::Write => write(b, convert_slice(c as *const u8, d)?), + Call::Open => open(convert_slice(b as *const u8, c)?, d), + Call::Close => close(b), + Call::Exec => exec(convert_slice(b as *const u8, c)?, convert_slice(d as *const [usize; 2], e)?), + Call::GetPid => getpid(), + Call::Dup => dup(b), + Call::Brk => brk(b), + Call::Iopl => iopl(b), + Call::Clone => clone(b, stack), + Call::SchedYield => sched_yield() + }, + Err(err) => { + println!("Unknown syscall {}", a); + Err(err) + } } } -} -#[no_mangle] -pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> usize { - match handle(a, b, c, d, e, f) { + match inner(a, b, c, d, e, f, stack) { Ok(value) => value, Err(value) => (-(value as isize)) as usize } diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index ad1d107..c4f846b 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -1,5 +1,6 @@ ///! Process syscalls +use core::mem; use core::str; use arch; @@ -42,9 +43,52 @@ pub fn brk(address: usize) -> Result { } } -pub fn clone(flags: usize) -> Result { - println!("Clone {:X}", flags); - Ok(0) +pub fn clone(flags: usize, stack_base: usize) -> Result { + println!("Clone {:X}: {:X}", flags, stack_base); + + let arch; + let mut stack_option = None; + let mut offset = 0; + + // Copy from old process + { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::NoProcess)?; + let context = context_lock.read(); + arch = context.arch.clone(); + if let Some(ref stack) = context.kstack { + offset = stack_base - stack.as_ptr() as usize - mem::size_of::(); // Add clone ret + let mut new_stack = stack.clone(); + unsafe { + let func_ptr = new_stack.as_mut_ptr().offset(offset as isize); + *(func_ptr as *mut usize) = arch::interrupt::syscall::clone_ret as usize; + } + stack_option = Some(new_stack); + } + } + + // Set up new process + let pid; + { + let mut contexts = context::contexts_mut(); + let context_lock = contexts.new_context()?; + let mut context = context_lock.write(); + context.arch = arch; + if let Some(stack) = stack_option.take() { + context.arch.set_stack(stack.as_ptr() as usize + offset); + context.kstack = Some(stack); + } + context.blocked = false; + pid = context.id; + } + + println!("Clone {}", pid); + + unsafe { asm!("xchg bx, bx" : : : : "intel", "volatile"); } + + unsafe { context::switch(); } + + Ok(pid) } pub fn exit(status: usize) -> ! {