Bare-bones ptracing functionality
Since even a very basic ptrace can be nice to have, I thought I would split the, perhaps rather big, ptrace project up in multiple PRs to make as few changes as necessary in each. This PR contains the initial registry modifying bits and only a very basic security measure. Letting this out to the community should be good for spotting bugs and maybe getting some hype ;)
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
use crate::interrupt::stack_trace;
|
||||
use crate::syscall::flag::*;
|
||||
use crate::{
|
||||
common::unique::Unique,
|
||||
context,
|
||||
interrupt::stack_trace,
|
||||
ptrace,
|
||||
syscall::flag::*
|
||||
};
|
||||
|
||||
extern {
|
||||
fn ksignal(signal: usize);
|
||||
@@ -13,9 +18,41 @@ interrupt_stack_p!(divide_by_zero, stack, {
|
||||
});
|
||||
|
||||
interrupt_stack!(debug, stack, {
|
||||
println!("Debug trap");
|
||||
stack.dump();
|
||||
ksignal(SIGTRAP);
|
||||
match ptrace::breakpoint_callback_dryrun(true) {
|
||||
Some(_) => {
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
if let Some(ref mut kstack) = context.kstack {
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(stack)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let had_singlestep = stack.iret.rflags & (1 << 8) == 1 << 8;
|
||||
stack.set_singlestep(false);
|
||||
if ptrace::breakpoint_callback(true).is_none() {
|
||||
// There is no guarantee that this is Some(_) just
|
||||
// because the dryrun is Some(_). So, if there wasn't
|
||||
// *actually* any breakpoint, restore the trap flag.
|
||||
stack.set_singlestep(had_singlestep);
|
||||
}
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
context.regs = None;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
println!("Debug trap");
|
||||
stack.dump();
|
||||
ksignal(SIGTRAP);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
interrupt_stack!(non_maskable, stack, {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::common::unique::Unique;
|
||||
use crate::context;
|
||||
use crate::context::timeout;
|
||||
use crate::device::pic;
|
||||
@@ -40,7 +41,7 @@ pub unsafe fn acknowledge(irq: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
interrupt!(pit, {
|
||||
interrupt_stack!(pit, stack, {
|
||||
// Saves CPU time by not sending IRQ event irq_trigger(0);
|
||||
|
||||
const PIT_RATE: u64 = 2_250_286;
|
||||
@@ -61,7 +62,25 @@ interrupt!(pit, {
|
||||
timeout::trigger();
|
||||
|
||||
if PIT_TICKS.fetch_add(1, Ordering::SeqCst) >= 10 {
|
||||
{
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
// Make all registers available to e.g. the proc:
|
||||
// scheme
|
||||
if let Some(ref mut kstack) = context.kstack {
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(stack)));
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = context::switch();
|
||||
{
|
||||
let contexts = crate::context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
context.regs = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,25 +1,74 @@
|
||||
use crate::arch::macros::InterruptStack;
|
||||
use crate::arch::{gdt, pti};
|
||||
use crate::syscall;
|
||||
use crate::common::unique::Unique;
|
||||
use crate::{context, ptrace, syscall};
|
||||
use x86::shared::msr;
|
||||
|
||||
pub unsafe fn init() {
|
||||
msr::wrmsr(msr::IA32_STAR, ((gdt::GDT_KERNEL_CODE as u64) << 3) << 32);
|
||||
msr::wrmsr(msr::IA32_LSTAR, syscall_instruction as u64);
|
||||
msr::wrmsr(msr::IA32_FMASK, 1 << 9);
|
||||
msr::wrmsr(msr::IA32_FMASK, 0x0300); // Clear trap flag and interrupt enable
|
||||
msr::wrmsr(msr::IA32_KERNEL_GS_BASE, &gdt::TSS as *const _ as u64);
|
||||
|
||||
let efer = msr::rdmsr(msr::IA32_EFER);
|
||||
msr::wrmsr(msr::IA32_EFER, efer | 1);
|
||||
}
|
||||
|
||||
// Not a function pointer because it somehow messes up the returning
|
||||
// from clone() (via clone_ret()). Not sure what the problem is.
|
||||
macro_rules! with_interrupt_stack {
|
||||
(unsafe fn $wrapped:ident($stack:ident) -> usize $code:block) => {
|
||||
/// Because of how clones work, we need a function that returns a
|
||||
/// usize. Here, `inner` will be this function. The child process in a
|
||||
/// clone will terminate this function with a 0 return value, and it
|
||||
/// might also have updated the interrupt_stack pointer.
|
||||
#[inline(never)]
|
||||
unsafe fn $wrapped(stack: *mut SyscallStack) {
|
||||
let stack = &mut *stack;
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
if let Some(ref mut kstack) = context.kstack {
|
||||
context.regs = Some((kstack.as_mut_ptr() as usize, Unique::new_unchecked(&mut stack.interrupt_stack)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_sysemu = ptrace::breakpoint_callback(false);
|
||||
if !is_sysemu.unwrap_or(false) {
|
||||
// If not on a sysemu breakpoint
|
||||
let $stack = &mut *stack;
|
||||
$stack.interrupt_stack.scratch.rax = $code;
|
||||
|
||||
if is_sysemu.is_some() {
|
||||
// Only callback if there was a pre-syscall
|
||||
// callback too.
|
||||
ptrace::breakpoint_callback(false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.current() {
|
||||
let mut context = context.write();
|
||||
context.regs = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern fn syscall_instruction() {
|
||||
#[inline(never)]
|
||||
unsafe fn inner(stack: &mut SyscallStack) -> usize {
|
||||
let rbp;
|
||||
asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
|
||||
with_interrupt_stack! {
|
||||
unsafe fn inner(stack) -> usize {
|
||||
let rbp;
|
||||
asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
|
||||
|
||||
syscall::syscall(stack.rax, stack.rdi, stack.rsi, stack.rdx, stack.r10, stack.r8, rbp, stack)
|
||||
let scratch = &stack.interrupt_stack.scratch;
|
||||
syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack)
|
||||
}
|
||||
}
|
||||
|
||||
// Yes, this is magic. No, you don't need to understand
|
||||
@@ -40,76 +89,12 @@ pub unsafe extern fn syscall_instruction() {
|
||||
:
|
||||
: "intel", "volatile");
|
||||
|
||||
// Push scratch registers
|
||||
asm!("push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rdi
|
||||
push rsi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push fs
|
||||
mov r11, 0x18
|
||||
mov fs, r11"
|
||||
: : : : "intel", "volatile");
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
|
||||
|
||||
// Map kernel
|
||||
pti::map();
|
||||
|
||||
let a = inner(&mut *(rsp as *mut SyscallStack));
|
||||
|
||||
// Unmap kernel
|
||||
pti::unmap();
|
||||
|
||||
asm!("" : : "{rax}"(a) : : "intel", "volatile");
|
||||
|
||||
// Interrupt return
|
||||
asm!("pop fs
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rbx
|
||||
add rsp, 8
|
||||
iretq"
|
||||
: : : : "intel", "volatile");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern fn syscall() {
|
||||
#[inline(never)]
|
||||
unsafe fn inner(stack: &mut SyscallStack) -> usize {
|
||||
let rbp;
|
||||
asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
|
||||
|
||||
syscall::syscall(stack.rax, stack.rbx, stack.rcx, stack.rdx, stack.rsi, stack.rdi, rbp, stack)
|
||||
}
|
||||
|
||||
// Push scratch registers
|
||||
asm!("push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rdi
|
||||
push rsi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push fs
|
||||
scratch_push!();
|
||||
asm!("push fs
|
||||
mov r11, 0x18
|
||||
mov fs, r11"
|
||||
mov fs, r11
|
||||
push rbx"
|
||||
: : : : "intel", "volatile");
|
||||
|
||||
// Get reference to stack variables
|
||||
@@ -119,56 +104,81 @@ pub unsafe extern fn syscall() {
|
||||
// Map kernel
|
||||
pti::map();
|
||||
|
||||
let a = inner(&mut *(rsp as *mut SyscallStack));
|
||||
inner(rsp as *mut SyscallStack);
|
||||
|
||||
// Unmap kernel
|
||||
pti::unmap();
|
||||
|
||||
asm!("" : : "{rax}"(a) : : "intel", "volatile");
|
||||
// Interrupt return
|
||||
asm!("pop rbx
|
||||
pop fs"
|
||||
: : : : "intel", "volatile");
|
||||
scratch_pop!();
|
||||
asm!("iretq" : : : : "intel", "volatile");
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern fn syscall() {
|
||||
with_interrupt_stack! {
|
||||
unsafe fn inner(stack) -> usize {
|
||||
let rbp;
|
||||
asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
|
||||
|
||||
let scratch = &stack.interrupt_stack.scratch;
|
||||
syscall::syscall(scratch.rax, stack.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack)
|
||||
}
|
||||
}
|
||||
|
||||
// Push scratch registers
|
||||
scratch_push!();
|
||||
asm!("push fs
|
||||
mov r11, 0x18
|
||||
mov fs, r11
|
||||
push rbx"
|
||||
: : : : "intel", "volatile");
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
|
||||
|
||||
// Map kernel
|
||||
pti::map();
|
||||
|
||||
inner(rsp as *mut SyscallStack);
|
||||
|
||||
// Unmap kernel
|
||||
pti::unmap();
|
||||
|
||||
// Interrupt return
|
||||
asm!("pop fs
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rbx
|
||||
add rsp, 8
|
||||
iretq"
|
||||
: : : : "intel", "volatile");
|
||||
asm!("pop rbx
|
||||
pop fs"
|
||||
: : : : "intel", "volatile");
|
||||
scratch_pop!();
|
||||
asm!("iretq" : : : : "intel", "volatile");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
pub struct SyscallStack {
|
||||
pub fs: usize,
|
||||
pub r11: usize,
|
||||
pub r10: usize,
|
||||
pub r9: usize,
|
||||
pub r8: usize,
|
||||
pub rsi: usize,
|
||||
pub rdi: usize,
|
||||
pub rdx: usize,
|
||||
pub rcx: usize,
|
||||
pub rbx: usize,
|
||||
pub rax: usize,
|
||||
pub rip: usize,
|
||||
pub cs: usize,
|
||||
pub rflags: usize,
|
||||
pub interrupt_stack: InterruptStack,
|
||||
|
||||
// Will only be present if syscall is called from another ring
|
||||
pub rsp: usize,
|
||||
pub ss: usize,
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe extern fn clone_ret() {
|
||||
asm!("
|
||||
pop rbp
|
||||
xor rax, rax
|
||||
"
|
||||
: : : : "intel", "volatile");
|
||||
pub unsafe extern "C" fn clone_ret() {
|
||||
// The C x86_64 ABI specifies that rbp is pushed to save the old
|
||||
// call frame. Popping rbp means we're using the parent's call
|
||||
// frame and thus will not only return from this function but also
|
||||
// from the function above this one.
|
||||
// When this is called, the stack should have been
|
||||
// interrupt->inner->syscall->clone
|
||||
// then changed to
|
||||
// interrupt->inner->clone_ret->clone
|
||||
// so this will return from "inner".
|
||||
|
||||
asm!("pop rbp" : : : : "intel", "volatile");
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use syscall::data::IntRegisters;
|
||||
|
||||
/// Print to console
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
@@ -204,6 +206,50 @@ impl InterruptStack {
|
||||
self.scratch.dump();
|
||||
println!("FS: {:>016X}", { self.fs });
|
||||
}
|
||||
/// Saves all registers to a struct used by the proc:
|
||||
/// scheme to read/write registers.
|
||||
pub fn save(&self, all: &mut IntRegisters) {
|
||||
all.fs = self.fs;
|
||||
all.r11 = self.scratch.r11;
|
||||
all.r10 = self.scratch.r10;
|
||||
all.r9 = self.scratch.r9;
|
||||
all.r8 = self.scratch.r8;
|
||||
all.rsi = self.scratch.rsi;
|
||||
all.rdi = self.scratch.rdi;
|
||||
all.rdx = self.scratch.rdx;
|
||||
all.rcx = self.scratch.rcx;
|
||||
all.rax = self.scratch.rax;
|
||||
all.rip = self.iret.rip;
|
||||
all.cs = self.iret.cs;
|
||||
all.eflags = self.iret.rflags;
|
||||
}
|
||||
/// Loads all registers from a struct used by the proc:
|
||||
/// scheme to read/write registers.
|
||||
pub fn load(&mut self, all: &IntRegisters) {
|
||||
self.fs = all.fs;
|
||||
self.scratch.r11 = all.r11;
|
||||
self.scratch.r10 = all.r10;
|
||||
self.scratch.r9 = all.r9;
|
||||
self.scratch.r8 = all.r8;
|
||||
self.scratch.rsi = all.rsi;
|
||||
self.scratch.rdi = all.rdi;
|
||||
self.scratch.rdx = all.rdx;
|
||||
self.scratch.rcx = all.rcx;
|
||||
self.scratch.rax = all.rax;
|
||||
self.iret.rip = all.rip;
|
||||
self.iret.cs = all.cs;
|
||||
self.iret.rflags = all.eflags;
|
||||
}
|
||||
/// Enables the "Trap Flag" in the FLAGS register, causing the CPU
|
||||
/// to send a Debug exception after the next instruction. This is
|
||||
/// used for singlestep in the proc: scheme.
|
||||
pub fn set_singlestep(&mut self, enabled: bool) {
|
||||
if enabled {
|
||||
self.iret.rflags |= 1 << 8;
|
||||
} else {
|
||||
self.iret.rflags &= !(1 << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
#[macro_use]
|
||||
pub mod int_like;
|
||||
pub mod unique;
|
||||
|
||||
33
src/common/unique.rs
Normal file
33
src/common/unique.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use core::{fmt, ptr::NonNull};
|
||||
|
||||
/// A small wrapper around NonNull<T> that is Send + Sync, which is
|
||||
/// only correct if the pointer is never accessed from multiple
|
||||
/// locations across threads. Which is always, if the pointer is
|
||||
/// unique.
|
||||
pub struct Unique<T>(NonNull<T>);
|
||||
|
||||
impl<T> Copy for Unique<T> {}
|
||||
impl<T> Clone for Unique<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
unsafe impl<T> Send for Unique<T> {}
|
||||
unsafe impl<T> Sync for Unique<T> {}
|
||||
|
||||
impl<T> Unique<T> {
|
||||
pub fn new(ptr: *mut T) -> Self {
|
||||
Self(NonNull::new(ptr).unwrap())
|
||||
}
|
||||
pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
||||
Self(NonNull::new_unchecked(ptr))
|
||||
}
|
||||
pub fn as_ptr(&self) -> *mut T {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
}
|
||||
impl<T> fmt::Debug for Unique<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,16 @@ use core::cmp::Ordering;
|
||||
use core::mem;
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::arch::paging::PAGE_SIZE;
|
||||
use crate::arch::{macros::InterruptStack, paging::PAGE_SIZE};
|
||||
use crate::common::unique::Unique;
|
||||
use crate::context::arch;
|
||||
use crate::context::file::FileDescriptor;
|
||||
use crate::context::memory::{Grant, Memory, SharedMemory, Tls};
|
||||
use crate::ipi::{ipi, IpiKind, IpiTarget};
|
||||
use crate::scheme::{SchemeNamespace, FileHandle};
|
||||
use crate::sync::WaitMap;
|
||||
use crate::syscall::data::SigAction;
|
||||
use crate::syscall::flag::SIG_DFL;
|
||||
use crate::sync::WaitMap;
|
||||
|
||||
/// Unique identifier for a context (i.e. `pid`).
|
||||
use ::core::sync::atomic::AtomicUsize;
|
||||
@@ -165,6 +166,15 @@ pub struct Context {
|
||||
pub files: Arc<Mutex<Vec<Option<FileDescriptor>>>>,
|
||||
/// Signal actions
|
||||
pub actions: Arc<Mutex<Vec<(SigAction, usize)>>>,
|
||||
/// The pointer to the user-space registers, saved after certain
|
||||
/// interrupts. This pointer is somewhere inside kstack, and the
|
||||
/// kstack address at the time of creation is the first element in
|
||||
/// this tuple.
|
||||
pub regs: Option<(usize, Unique<InterruptStack>)>,
|
||||
/// A somewhat hacky way to initially stop a context when creating
|
||||
/// a new instance of the proc: scheme, entirely separate from
|
||||
/// signals or any other way to restart a process.
|
||||
pub ptrace_stop: bool
|
||||
}
|
||||
|
||||
impl Context {
|
||||
@@ -216,6 +226,8 @@ impl Context {
|
||||
},
|
||||
0
|
||||
); 128])),
|
||||
regs: None,
|
||||
ptrace_stop: false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ unsafe fn update(context: &mut Context, cpu_id: usize) {
|
||||
|
||||
unsafe fn runnable(context: &Context, cpu_id: usize) -> bool {
|
||||
// Switch to context if it needs to run, is not currently running, and is owned by the current CPU
|
||||
!context.running && context.status == Status::Runnable && context.cpu_id == Some(cpu_id)
|
||||
!context.running && !context.ptrace_stop && context.status == Status::Runnable && context.cpu_id == Some(cpu_id)
|
||||
}
|
||||
|
||||
/// Switch to the next context
|
||||
|
||||
@@ -93,6 +93,9 @@ pub mod memory;
|
||||
#[cfg(not(any(feature="doc", test)))]
|
||||
pub mod panic;
|
||||
|
||||
/// Process tracing
|
||||
pub mod ptrace;
|
||||
|
||||
/// Schemes, filesystem handlers
|
||||
pub mod scheme;
|
||||
|
||||
|
||||
208
src/ptrace.rs
Normal file
208
src/ptrace.rs
Normal file
@@ -0,0 +1,208 @@
|
||||
use crate::{
|
||||
arch::macros::InterruptStack,
|
||||
common::unique::Unique,
|
||||
context::{self, Context, ContextId, Status},
|
||||
sync::WaitCondition
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
sync::Arc
|
||||
};
|
||||
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use syscall::error::*;
|
||||
|
||||
// ____ _ _ _
|
||||
// | __ ) _ __ ___ __ _| | ___ __ ___ (_)_ __ | |_ ___
|
||||
// | _ \| '__/ _ \/ _` | |/ / '_ \ / _ \| | '_ \| __/ __|
|
||||
// | |_) | | | __/ (_| | <| |_) | (_) | | | | | |_\__ \
|
||||
// |____/|_| \___|\__,_|_|\_\ .__/ \___/|_|_| |_|\__|___/
|
||||
// |_|
|
||||
|
||||
struct Handle {
|
||||
tracee: Arc<WaitCondition>,
|
||||
tracer: Arc<WaitCondition>,
|
||||
reached: bool,
|
||||
|
||||
sysemu: bool,
|
||||
singlestep: bool
|
||||
}
|
||||
|
||||
static BREAKPOINTS: Once<RwLock<BTreeMap<ContextId, Handle>>> = Once::new();
|
||||
|
||||
fn init_breakpoints() -> RwLock<BTreeMap<ContextId, Handle>> {
|
||||
RwLock::new(BTreeMap::new())
|
||||
}
|
||||
fn breakpoints() -> RwLockReadGuard<'static, BTreeMap<ContextId, Handle>> {
|
||||
BREAKPOINTS.call_once(init_breakpoints).read()
|
||||
}
|
||||
fn breakpoints_mut() -> RwLockWriteGuard<'static, BTreeMap<ContextId, Handle>> {
|
||||
BREAKPOINTS.call_once(init_breakpoints).write()
|
||||
}
|
||||
|
||||
fn inner_cont(pid: ContextId) -> Option<Handle> {
|
||||
// Remove the breakpoint to both save space and also make sure any
|
||||
// yet unreached but obsolete breakpoints don't stop the program.
|
||||
let handle = breakpoints_mut().remove(&pid)?;
|
||||
handle.tracee.notify();
|
||||
Some(handle)
|
||||
}
|
||||
|
||||
/// Continue the process with the specified ID
|
||||
pub fn cont(pid: ContextId) {
|
||||
inner_cont(pid);
|
||||
}
|
||||
|
||||
/// Create a new breakpoint for the specified tracee, optionally with a sysemu flag
|
||||
pub fn set_breakpoint(pid: ContextId, sysemu: bool, singlestep: bool) {
|
||||
let (tracee, tracer) = match inner_cont(pid) {
|
||||
Some(breakpoint) => (breakpoint.tracee, breakpoint.tracer),
|
||||
None => (
|
||||
Arc::new(WaitCondition::new()),
|
||||
Arc::new(WaitCondition::new())
|
||||
)
|
||||
};
|
||||
|
||||
breakpoints_mut().insert(pid, Handle {
|
||||
tracee,
|
||||
tracer,
|
||||
reached: false,
|
||||
sysemu,
|
||||
singlestep
|
||||
});
|
||||
}
|
||||
|
||||
/// Wait for the tracee to stop.
|
||||
/// Note: Don't call while holding any locks, this will switch contexts
|
||||
pub fn wait_breakpoint(pid: ContextId) -> Result<()> {
|
||||
let tracer = {
|
||||
let breakpoints = breakpoints();
|
||||
match breakpoints.get(&pid) {
|
||||
Some(breakpoint) if !breakpoint.reached => Arc::clone(&breakpoint.tracer),
|
||||
_ => return Ok(())
|
||||
}
|
||||
};
|
||||
while !tracer.wait() {}
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = context.read();
|
||||
if let Status::Exited(_) = context.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the same value as breakpoint_callback would do, but
|
||||
/// doesn't actually perform the action. You should not rely too
|
||||
/// heavily on this value, as the lock *is* released between this call
|
||||
/// and another.
|
||||
pub fn breakpoint_callback_dryrun(singlestep: bool) -> Option<bool> {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current()?;
|
||||
let context = context.read();
|
||||
|
||||
let breakpoints = breakpoints();
|
||||
let breakpoint = breakpoints.get(&context.id)?;
|
||||
if breakpoint.singlestep != singlestep {
|
||||
return None;
|
||||
}
|
||||
Some(breakpoint.sysemu)
|
||||
}
|
||||
|
||||
/// Notify the tracer and await green flag to continue.
|
||||
/// Note: Don't call while holding any locks, this will switch contexts
|
||||
pub fn breakpoint_callback(singlestep: bool) -> Option<bool> {
|
||||
// Can't hold any locks when executing wait()
|
||||
let (tracee, sysemu) = {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current()?;
|
||||
let context = context.read();
|
||||
|
||||
let mut breakpoints = breakpoints_mut();
|
||||
let breakpoint = breakpoints.get_mut(&context.id)?;
|
||||
|
||||
// TODO: How should singlesteps interact with syscalls? How
|
||||
// does Linux handle this?
|
||||
|
||||
// if singlestep && !breakpoint.singlestep {
|
||||
if breakpoint.singlestep != singlestep {
|
||||
return None;
|
||||
}
|
||||
|
||||
breakpoint.tracer.notify();
|
||||
// In case no tracer is waiting, make sure the next one gets
|
||||
// the memo
|
||||
breakpoint.reached = true;
|
||||
|
||||
(
|
||||
Arc::clone(&breakpoint.tracee),
|
||||
breakpoint.sysemu
|
||||
)
|
||||
};
|
||||
|
||||
while !tracee.wait() {}
|
||||
|
||||
Some(sysemu)
|
||||
}
|
||||
|
||||
/// Call when a context is closed to alert any tracers
|
||||
pub fn close(pid: ContextId) {
|
||||
{
|
||||
let breakpoints = breakpoints();
|
||||
if let Some(breakpoint) = breakpoints.get(&pid) {
|
||||
breakpoint.tracer.notify();
|
||||
}
|
||||
}
|
||||
|
||||
breakpoints_mut().remove(&pid);
|
||||
}
|
||||
|
||||
// ____ _ _
|
||||
// | _ \ ___ __ _(_)___| |_ ___ _ __ ___
|
||||
// | |_) / _ \/ _` | / __| __/ _ \ '__/ __|
|
||||
// | _ < __/ (_| | \__ \ || __/ | \__ \
|
||||
// |_| \_\___|\__, |_|___/\__\___|_| |___/
|
||||
// |___/
|
||||
|
||||
/// Return the InterruptStack pointer, but relative to the specified
|
||||
/// stack instead of the original.
|
||||
pub unsafe fn rebase_regs_ptr(
|
||||
regs: Option<(usize, Unique<InterruptStack>)>,
|
||||
kstack: Option<&Box<[u8]>>
|
||||
) -> Option<*const InterruptStack> {
|
||||
let (old_base, ptr) = regs?;
|
||||
let new_base = kstack?.as_ptr() as usize;
|
||||
Some((ptr.as_ptr() as usize - old_base + new_base) as *const _)
|
||||
}
|
||||
/// Return the InterruptStack pointer, but relative to the specified
|
||||
/// stack instead of the original.
|
||||
pub unsafe fn rebase_regs_ptr_mut(
|
||||
regs: Option<(usize, Unique<InterruptStack>)>,
|
||||
kstack: Option<&mut Box<[u8]>>
|
||||
) -> Option<*mut InterruptStack> {
|
||||
let (old_base, ptr) = regs?;
|
||||
let new_base = kstack?.as_mut_ptr() as usize;
|
||||
Some((ptr.as_ptr() as usize - old_base + new_base) as *mut _)
|
||||
}
|
||||
|
||||
/// Return a reference to the InterruptStack struct in memory. If the
|
||||
/// kernel stack has been backed up by a signal handler, this instead
|
||||
/// returns the struct inside that memory, as that will later be
|
||||
/// restored and otherwise undo all your changes. See `update(...)` in
|
||||
/// context/switch.rs.
|
||||
pub unsafe fn regs_for(context: &Context) -> Option<&InterruptStack> {
|
||||
Some(&*match context.ksig {
|
||||
Some((_, _, ref kstack)) => rebase_regs_ptr(context.regs, kstack.as_ref())?,
|
||||
None => context.regs?.1.as_ptr()
|
||||
})
|
||||
}
|
||||
|
||||
/// Mutable version of `regs_for`
|
||||
pub unsafe fn regs_for_mut(context: &mut Context) -> Option<&mut InterruptStack> {
|
||||
Some(&mut *match context.ksig {
|
||||
Some((_, _, ref mut kstack)) => rebase_regs_ptr_mut(context.regs, kstack.as_mut())?,
|
||||
None => context.regs?.1.as_ptr()
|
||||
})
|
||||
}
|
||||
@@ -22,6 +22,7 @@ use self::irq::IrqScheme;
|
||||
use self::itimer::ITimerScheme;
|
||||
use self::memory::MemoryScheme;
|
||||
use self::pipe::PipeScheme;
|
||||
use self::proc::ProcScheme;
|
||||
use self::root::RootScheme;
|
||||
use self::sys::SysScheme;
|
||||
use self::time::TimeScheme;
|
||||
@@ -51,6 +52,9 @@ pub mod memory;
|
||||
/// `pipe:` - used internally by the kernel to implement `pipe`
|
||||
pub mod pipe;
|
||||
|
||||
/// `proc:` - allows tracing processes and reading/writing their memory
|
||||
pub mod proc;
|
||||
|
||||
/// `:` - allows the creation of userspace schemes, tightly dependent on `user`
|
||||
pub mod root;
|
||||
|
||||
@@ -128,29 +132,21 @@ impl SchemeList {
|
||||
}
|
||||
|
||||
/// Initialize the root namespace
|
||||
#[cfg(not(feature="live"))]
|
||||
fn new_root(&mut self) {
|
||||
// Do common namespace initialization
|
||||
let ns = self.new_ns();
|
||||
|
||||
// Debug, Initfs and IRQ are only available in the root namespace. Pipe is special
|
||||
// These schemes should only be available on the root
|
||||
self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(Box::new(PipeScheme::new(scheme_id)))).unwrap();
|
||||
}
|
||||
self.insert(ns, Box::new(*b"proc"), |_| Arc::new(Box::new(ProcScheme::new()))).unwrap();
|
||||
|
||||
/// Initialize the root namespace - with live disk
|
||||
#[cfg(feature="live")]
|
||||
fn new_root(&mut self) {
|
||||
// Do common namespace initialization
|
||||
let ns = self.new_ns();
|
||||
#[cfg(feature = "live")] {
|
||||
self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap();
|
||||
}
|
||||
|
||||
// Debug, Disk, Initfs and IRQ are only available in the root namespace. Pipe is special
|
||||
self.insert(ns, Box::new(*b"debug"), |scheme_id| Arc::new(Box::new(DebugScheme::new(scheme_id)))).unwrap();
|
||||
self.insert(ns, Box::new(*b"disk/live"), |_| Arc::new(Box::new(self::live::DiskScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"initfs"), |_| Arc::new(Box::new(InitFsScheme::new()))).unwrap();
|
||||
self.insert(ns, Box::new(*b"irq"), |scheme_id| Arc::new(Box::new(IrqScheme::new(scheme_id)))).unwrap();
|
||||
// Pipe is special and needs to be in the root namespace
|
||||
self.insert(ns, Box::new(*b"pipe"), |scheme_id| Arc::new(Box::new(PipeScheme::new(scheme_id)))).unwrap();
|
||||
}
|
||||
|
||||
|
||||
351
src/scheme/proc.rs
Normal file
351
src/scheme/proc.rs
Normal file
@@ -0,0 +1,351 @@
|
||||
use crate::{
|
||||
context::{self, ContextId, Status},
|
||||
ptrace
|
||||
};
|
||||
|
||||
use alloc::collections::{BTreeMap, BTreeSet};
|
||||
use core::{
|
||||
cmp,
|
||||
mem,
|
||||
slice,
|
||||
sync::atomic::{AtomicUsize, Ordering}
|
||||
};
|
||||
use spin::{Mutex, RwLock};
|
||||
use syscall::{
|
||||
data::{IntRegisters, FloatRegisters},
|
||||
error::*,
|
||||
flag::*,
|
||||
scheme::Scheme
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum RegsKind {
|
||||
Float,
|
||||
Int
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
enum Operation {
|
||||
Memory,
|
||||
Regs(RegsKind),
|
||||
Trace
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Handle {
|
||||
flags: usize,
|
||||
pid: ContextId,
|
||||
operation: Operation
|
||||
}
|
||||
|
||||
pub struct ProcScheme {
|
||||
next_id: AtomicUsize,
|
||||
handles: RwLock<BTreeMap<usize, Handle>>,
|
||||
traced: Mutex<BTreeSet<ContextId>>
|
||||
}
|
||||
|
||||
impl ProcScheme {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
next_id: AtomicUsize::new(0),
|
||||
handles: RwLock::new(BTreeMap::new()),
|
||||
traced: Mutex::new(BTreeSet::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for ProcScheme {
|
||||
fn open(&self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||
let path = core::str::from_utf8(path).map_err(|_| Error::new(EINVAL))?;
|
||||
let mut parts = path.splitn(2, '/');
|
||||
let pid = parts.next()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.map(ContextId::from)
|
||||
.ok_or(Error::new(EINVAL))?;
|
||||
let operation = match parts.next() {
|
||||
Some("mem") => Operation::Memory,
|
||||
Some("regs/float") => Operation::Regs(RegsKind::Float),
|
||||
Some("regs/int") => Operation::Regs(RegsKind::Int),
|
||||
Some("trace") => Operation::Trace,
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
|
||||
{
|
||||
// TODO: Put better security here?
|
||||
|
||||
let context = context.read();
|
||||
if uid != 0 && gid != 0
|
||||
&& uid != context.euid && gid != context.egid {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
}
|
||||
|
||||
if let Operation::Trace = operation {
|
||||
let mut traced = self.traced.lock();
|
||||
|
||||
if traced.contains(&pid) {
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
traced.insert(pid);
|
||||
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = true;
|
||||
}
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
flags,
|
||||
pid,
|
||||
operation
|
||||
});
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Using dup for `proc:` simply opens another operation on the same PID
|
||||
/// ```rust,ignore
|
||||
/// let trace = syscall::open("proc:1234/trace")?;
|
||||
///
|
||||
/// // let regs = syscall::open("proc:1234/regs/int")?;
|
||||
/// let regs = syscall::dup(trace, "regs/int")?;
|
||||
/// ```
|
||||
fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&old_id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
let mut path = format!("{}/", handle.pid.into()).into_bytes();
|
||||
path.extend_from_slice(buf);
|
||||
|
||||
let (uid, gid) = {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context.read();
|
||||
(context.euid, context.egid)
|
||||
};
|
||||
|
||||
self.open(&path, handle.flags, uid, gid)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
// Can't hold locks during the context switch later when
|
||||
// waiting for a process to stop running.
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
match handle.operation {
|
||||
Operation::Memory => {
|
||||
// let contexts = context::contexts();
|
||||
// let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
// let context = context.read();
|
||||
|
||||
// for grant in &*context.grants.lock() {
|
||||
// println!("Grant: {} -> {}", grant.start.get(), grant.size);
|
||||
// }
|
||||
// unimplemented!();
|
||||
return Err(Error::new(EBADF));
|
||||
},
|
||||
Operation::Regs(kind) => {
|
||||
union Output {
|
||||
_float: FloatRegisters,
|
||||
int: IntRegisters
|
||||
}
|
||||
let mut first = true;
|
||||
let (output, size) = loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
let context = context.read();
|
||||
|
||||
break match kind {
|
||||
RegsKind::Float => {
|
||||
// TODO!!
|
||||
// (Output { float: FloatRegisters::default() }, mem::size_of::<FloatRegisters>())
|
||||
return Err(Error::new(EBADF));
|
||||
},
|
||||
RegsKind::Int => match unsafe { ptrace::regs_for(&context) } {
|
||||
None => {
|
||||
// Another CPU is running this process, wait until it's stopped.
|
||||
continue;
|
||||
},
|
||||
Some(stack) => {
|
||||
let mut regs = IntRegisters::default();
|
||||
|
||||
stack.save(&mut regs);
|
||||
|
||||
(Output { int: regs }, mem::size_of::<IntRegisters>())
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let bytes = unsafe {
|
||||
slice::from_raw_parts(&output as *const _ as *const u8, mem::size_of::<Output>())
|
||||
};
|
||||
let len = cmp::min(buf.len(), size);
|
||||
buf[..len].copy_from_slice(&bytes[..len]);
|
||||
|
||||
Ok(len)
|
||||
},
|
||||
Operation::Trace => Err(Error::new(EBADF))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
|
||||
// Can't hold locks during the context switch later when
|
||||
// waiting for a process to stop running.
|
||||
let handle = {
|
||||
let handles = self.handles.read();
|
||||
*handles.get(&id).ok_or(Error::new(EBADF))?
|
||||
};
|
||||
|
||||
let mut first = true;
|
||||
match handle.operation {
|
||||
Operation::Memory => {
|
||||
// unimplemented!()
|
||||
return Err(Error::new(EBADF));
|
||||
},
|
||||
Operation::Regs(kind) => loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context.write();
|
||||
|
||||
break match kind {
|
||||
RegsKind::Float => {
|
||||
// TODO!!
|
||||
unimplemented!();
|
||||
},
|
||||
RegsKind::Int => match unsafe { ptrace::regs_for_mut(&mut context) } {
|
||||
None => {
|
||||
// Another CPU is running this process, wait until it's stopped.
|
||||
continue;
|
||||
},
|
||||
Some(stack) => {
|
||||
if buf.len() < mem::size_of::<IntRegisters>() {
|
||||
return Ok(0);
|
||||
}
|
||||
let regs = unsafe {
|
||||
*(buf as *const _ as *const IntRegisters)
|
||||
};
|
||||
|
||||
stack.load(®s);
|
||||
|
||||
Ok(mem::size_of::<IntRegisters>())
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
Operation::Trace => {
|
||||
if buf.len() < 1 {
|
||||
return Ok(0);
|
||||
}
|
||||
let op = buf[0];
|
||||
let sysemu = op & PTRACE_SYSEMU == PTRACE_SYSEMU;
|
||||
|
||||
let mut blocking = handle.flags & O_NONBLOCK != O_NONBLOCK;
|
||||
let mut wait_breakpoint = false;
|
||||
let mut singlestep = false;
|
||||
|
||||
match op & PTRACE_OPERATIONMASK {
|
||||
PTRACE_CONT => { ptrace::cont(handle.pid); },
|
||||
PTRACE_SYSCALL | PTRACE_SINGLESTEP => { // <- not a bitwise OR
|
||||
singlestep = op & PTRACE_OPERATIONMASK == PTRACE_SINGLESTEP;
|
||||
ptrace::set_breakpoint(handle.pid, sysemu, singlestep);
|
||||
wait_breakpoint = true;
|
||||
},
|
||||
PTRACE_WAIT => {
|
||||
wait_breakpoint = true;
|
||||
blocking = true;
|
||||
},
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
loop {
|
||||
if !first {
|
||||
// We've tried this before, so lets wait before retrying
|
||||
unsafe { context::switch(); }
|
||||
}
|
||||
first = false;
|
||||
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.get(handle.pid).ok_or(Error::new(ESRCH))?;
|
||||
let mut context = context.write();
|
||||
if let Status::Exited(_) = context.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
|
||||
if singlestep {
|
||||
match unsafe { ptrace::regs_for_mut(&mut context) } {
|
||||
None => continue,
|
||||
Some(stack) => stack.set_singlestep(true)
|
||||
}
|
||||
}
|
||||
|
||||
context.ptrace_stop = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if wait_breakpoint && blocking {
|
||||
ptrace::wait_breakpoint(handle.pid)?;
|
||||
}
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match cmd {
|
||||
F_SETFL => { handle.flags = arg; Ok(0) },
|
||||
F_GETFL => return Ok(handle.flags),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let path = format!("proc:{}/{}", handle.pid.into(), match handle.operation {
|
||||
Operation::Memory => "mem",
|
||||
Operation::Regs(RegsKind::Float) => "regs/float",
|
||||
Operation::Regs(RegsKind::Int) => "regs/int",
|
||||
Operation::Trace => "trace"
|
||||
});
|
||||
|
||||
let len = cmp::min(path.len(), buf.len());
|
||||
buf[..len].copy_from_slice(&path.as_bytes()[..len]);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
let handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?;
|
||||
ptrace::cont(handle.pid);
|
||||
|
||||
let contexts = context::contexts();
|
||||
if let Some(context) = contexts.get(handle.pid) {
|
||||
let mut context = context.write();
|
||||
context.ptrace_stop = false;
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use core::mem;
|
||||
use core::ops::Range;
|
||||
use core::{ascii, mem};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
@@ -8,47 +7,13 @@ use super::flag::*;
|
||||
use super::number::*;
|
||||
use super::validate::*;
|
||||
|
||||
// Copied from std
|
||||
pub struct EscapeDefault {
|
||||
range: Range<usize>,
|
||||
data: [u8; 4],
|
||||
}
|
||||
|
||||
pub fn escape_default(c: u8) -> EscapeDefault {
|
||||
let (data, len) = match c {
|
||||
b'\t' => ([b'\\', b't', 0, 0], 2),
|
||||
b'\r' => ([b'\\', b'r', 0, 0], 2),
|
||||
b'\n' => ([b'\\', b'n', 0, 0], 2),
|
||||
b'\\' => ([b'\\', b'\\', 0, 0], 2),
|
||||
b'\'' => ([b'\\', b'\'', 0, 0], 2),
|
||||
b'"' => ([b'\\', b'"', 0, 0], 2),
|
||||
b'\x20' ... b'\x7e' => ([c, 0, 0, 0], 1),
|
||||
_ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4),
|
||||
};
|
||||
|
||||
return EscapeDefault { range: (0.. len), data: data };
|
||||
|
||||
fn hexify(b: u8) -> u8 {
|
||||
match b {
|
||||
0 ... 9 => b'0' + b,
|
||||
_ => b'a' + b - 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for EscapeDefault {
|
||||
type Item = u8;
|
||||
fn next(&mut self) -> Option<u8> { self.range.next().map(|i| self.data[i]) }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
|
||||
}
|
||||
|
||||
struct ByteStr<'a>(&'a[u8]);
|
||||
|
||||
impl<'a> ::core::fmt::Debug for ByteStr<'a> {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "\"")?;
|
||||
for i in self.0 {
|
||||
for ch in escape_default(*i) {
|
||||
for ch in ascii::escape_default(*i) {
|
||||
write!(f, "{}", ch as char)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ pub fn iopl(level: usize, stack: &mut SyscallStack) -> Result<usize> {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
stack.rflags = (stack.rflags & !(3 << 12)) | ((level & 3) << 12);
|
||||
let iret = &mut stack.interrupt_stack.iret;
|
||||
iret.rflags = (iret.rflags & !(3 << 12)) | ((level & 3) << 12);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -6,21 +6,21 @@ use core::{intrinsics, mem};
|
||||
use core::ops::DerefMut;
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::context::file::FileDescriptor;
|
||||
use crate::context::{ContextId, WaitpidKey};
|
||||
use crate::context;
|
||||
#[cfg(not(feature="doc"))]
|
||||
use crate::elf::{self, program_header};
|
||||
use crate::interrupt;
|
||||
use crate::ipi::{ipi, IpiKind, IpiTarget};
|
||||
use crate::memory::allocate_frames;
|
||||
use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE};
|
||||
use crate::paging::entry::EntryFlags;
|
||||
use crate::paging::mapper::MapperFlushAll;
|
||||
use crate::paging::temporary_page::TemporaryPage;
|
||||
use crate::start::usermode;
|
||||
use crate::interrupt;
|
||||
use crate::context;
|
||||
use crate::context::{ContextId, WaitpidKey};
|
||||
use crate::context::file::FileDescriptor;
|
||||
#[cfg(not(feature="doc"))]
|
||||
use crate::elf::{self, program_header};
|
||||
use crate::ipi::{ipi, IpiKind, IpiTarget};
|
||||
use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE};
|
||||
use crate::ptrace;
|
||||
use crate::scheme::FileHandle;
|
||||
use crate::syscall;
|
||||
use crate::start::usermode;
|
||||
use crate::syscall::data::{SigAction, Stat};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_STACK,
|
||||
@@ -28,6 +28,7 @@ use crate::syscall::flag::{CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_S
|
||||
SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, SIGCONT, SIGTERM,
|
||||
WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, wifstopped};
|
||||
use crate::syscall::validate::{validate_slice, validate_slice_mut};
|
||||
use crate::syscall;
|
||||
|
||||
pub fn brk(address: usize) -> Result<usize> {
|
||||
let contexts = context::contexts();
|
||||
@@ -128,10 +129,25 @@ pub fn clone(flags: usize, stack_base: usize) -> Result<ContextId> {
|
||||
}
|
||||
|
||||
if let Some(ref stack) = context.kstack {
|
||||
// Get the relative offset to the return address of this function
|
||||
// (base pointer - start of stack) - one
|
||||
offset = stack_base - stack.as_ptr() as usize - mem::size_of::<usize>(); // Add clone ret
|
||||
let mut new_stack = stack.clone();
|
||||
|
||||
unsafe {
|
||||
if let Some(regs) = ptrace::rebase_regs_ptr_mut(context.regs, Some(&mut new_stack)) {
|
||||
// We'll need to tell the clone that it should
|
||||
// return 0, but that's it. We don't actually
|
||||
// clone the registers, because it will then
|
||||
// become None and be exempt from all kinds of
|
||||
// ptracing until the current syscall has
|
||||
// completed.
|
||||
(*regs).scratch.rax = 0;
|
||||
}
|
||||
|
||||
// Change the return address of the child
|
||||
// (previously syscall) to the arch-specific
|
||||
// clone_ret callback
|
||||
let func_ptr = new_stack.as_mut_ptr().offset(offset as isize);
|
||||
*(func_ptr as *mut usize) = interrupt::syscall::clone_ret as usize;
|
||||
}
|
||||
@@ -1052,6 +1068,8 @@ pub fn exit(status: usize) -> ! {
|
||||
context.id
|
||||
};
|
||||
|
||||
ptrace::close(pid);
|
||||
|
||||
// Files must be closed while context is valid so that messages can be passed
|
||||
for (_fd, file_option) in close_files.drain(..).enumerate() {
|
||||
if let Some(file) = file_option {
|
||||
|
||||
2
syscall
2
syscall
Submodule syscall updated: 5cdc240d13...f8eda5ce1b
Reference in New Issue
Block a user