Merge remote-tracking branch 'origin/master' into signed-seek
This commit is contained in:
@@ -48,6 +48,19 @@ interrupt_stack!(non_maskable, stack, {
|
||||
});
|
||||
|
||||
interrupt_stack!(breakpoint, stack, {
|
||||
// The processor lets RIP point to the instruction *after* int3, so
|
||||
// unhandled breakpoint interrupt don't go in an infinite loop. But we
|
||||
// throw SIGTRAP anyway, so that's not a problem.
|
||||
//
|
||||
// We have the following code to prevent
|
||||
// - RIP from going out of sync with instructions
|
||||
// - The user having to do 2 syscalls to replace the instruction at RIP
|
||||
// - Having more compatibility glue for GDB than necessary
|
||||
//
|
||||
// Let's just follow Linux convention and let RIP be RIP-1, point to the
|
||||
// int3 instruction. After all, it's the sanest thing to do.
|
||||
stack.iret.rip -= 1;
|
||||
|
||||
let guard = ptrace::set_process_regs(stack);
|
||||
|
||||
if ptrace::breakpoint_callback(PTRACE_STOP_BREAKPOINT, None).is_none() {
|
||||
|
||||
@@ -67,12 +67,7 @@ pub unsafe extern fn syscall_instruction() {
|
||||
: "intel", "volatile");
|
||||
|
||||
// Push scratch registers
|
||||
scratch_push!();
|
||||
preserved_push!();
|
||||
asm!("push fs
|
||||
mov r11, 0x18
|
||||
mov fs, r11"
|
||||
: : : : "intel", "volatile");
|
||||
interrupt_push!();
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
@@ -87,10 +82,8 @@ pub unsafe extern fn syscall_instruction() {
|
||||
pti::unmap();
|
||||
|
||||
// Interrupt return
|
||||
asm!("pop fs" : : : : "intel", "volatile");
|
||||
preserved_pop!();
|
||||
scratch_pop!();
|
||||
asm!("iretq" : : : : "intel", "volatile");
|
||||
interrupt_pop!();
|
||||
iret!()
|
||||
}
|
||||
|
||||
#[naked]
|
||||
@@ -106,12 +99,7 @@ pub unsafe extern fn syscall() {
|
||||
}
|
||||
|
||||
// Push scratch registers
|
||||
scratch_push!();
|
||||
preserved_push!();
|
||||
asm!("push fs
|
||||
mov r11, 0x18
|
||||
mov fs, r11"
|
||||
: : : : "intel", "volatile");
|
||||
interrupt_push!();
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
@@ -126,10 +114,8 @@ pub unsafe extern fn syscall() {
|
||||
pti::unmap();
|
||||
|
||||
// Interrupt return
|
||||
asm!("pop fs" : : : : "intel", "volatile");
|
||||
preserved_pop!();
|
||||
scratch_pop!();
|
||||
asm!("iretq" : : : : "intel", "volatile");
|
||||
interrupt_pop!();
|
||||
iret!();
|
||||
}
|
||||
|
||||
#[naked]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::mem;
|
||||
use syscall::data::IntRegisters;
|
||||
use super::gdt;
|
||||
|
||||
/// Print to console
|
||||
#[macro_export]
|
||||
@@ -19,6 +20,7 @@ macro_rules! println {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct ScratchRegisters {
|
||||
pub r11: usize,
|
||||
@@ -77,6 +79,7 @@ macro_rules! scratch_pop {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct PreservedRegisters {
|
||||
pub r15: usize,
|
||||
@@ -124,9 +127,13 @@ macro_rules! preserved_pop {
|
||||
|
||||
macro_rules! fs_push {
|
||||
() => (asm!(
|
||||
"push fs
|
||||
"
|
||||
push fs
|
||||
|
||||
// Load kernel tls
|
||||
mov rax, 0x18
|
||||
mov fs, ax"
|
||||
mov fs, ax // can't load value directly into `fs`
|
||||
"
|
||||
: : : : "intel", "volatile"
|
||||
));
|
||||
}
|
||||
@@ -139,6 +146,7 @@ macro_rules! fs_pop {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct IretRegisters {
|
||||
pub rip: usize,
|
||||
@@ -198,6 +206,7 @@ macro_rules! interrupt {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct InterruptStack {
|
||||
pub fs: usize,
|
||||
@@ -207,6 +216,24 @@ pub struct InterruptStack {
|
||||
}
|
||||
|
||||
impl InterruptStack {
|
||||
pub fn new_usermode(ip: usize, sp: usize, arg: usize) -> Self {
|
||||
// See which registers are set in start.rs, function `usermode`
|
||||
Self {
|
||||
fs: gdt::GDT_USER_TLS << 3 | 3,
|
||||
preserved: PreservedRegisters::default(),
|
||||
scratch: ScratchRegisters {
|
||||
rdi: arg,
|
||||
..ScratchRegisters::default()
|
||||
},
|
||||
iret: IretRegisters {
|
||||
rip: ip,
|
||||
cs: gdt::GDT_USER_CODE << 3 | 3,
|
||||
rflags: 1 << 9,
|
||||
rsp: sp,
|
||||
ss: gdt::GDT_USER_DATA << 3 | 3,
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn dump(&self) {
|
||||
self.iret.dump();
|
||||
self.scratch.dump();
|
||||
@@ -298,9 +325,24 @@ impl InterruptStack {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! interrupt_push {
|
||||
() => {
|
||||
scratch_push!();
|
||||
preserved_push!();
|
||||
fs_push!();
|
||||
};
|
||||
}
|
||||
macro_rules! interrupt_pop {
|
||||
() => {
|
||||
fs_pop!();
|
||||
preserved_pop!();
|
||||
scratch_pop!();
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! interrupt_stack {
|
||||
($name:ident, $stack: ident, $func:block) => {
|
||||
($name:ident, $stack:ident, $func:block) => {
|
||||
#[naked]
|
||||
pub unsafe extern fn $name () {
|
||||
#[inline(never)]
|
||||
@@ -309,9 +351,7 @@ macro_rules! interrupt_stack {
|
||||
}
|
||||
|
||||
// Push scratch registers
|
||||
scratch_push!();
|
||||
preserved_push!();
|
||||
fs_push!();
|
||||
interrupt_push!();
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
@@ -327,15 +367,14 @@ macro_rules! interrupt_stack {
|
||||
$crate::arch::x86_64::pti::unmap();
|
||||
|
||||
// Pop scratch registers and return
|
||||
fs_pop!();
|
||||
preserved_pop!();
|
||||
scratch_pop!();
|
||||
interrupt_pop!();
|
||||
iret!();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default)]
|
||||
#[repr(packed)]
|
||||
pub struct InterruptErrorStack {
|
||||
pub fs: usize,
|
||||
@@ -366,9 +405,7 @@ macro_rules! interrupt_error {
|
||||
}
|
||||
|
||||
// Push scratch registers
|
||||
scratch_push!();
|
||||
preserved_push!();
|
||||
fs_push!();
|
||||
interrupt_push!();
|
||||
|
||||
// Get reference to stack variables
|
||||
let rsp: usize;
|
||||
@@ -384,10 +421,8 @@ macro_rules! interrupt_error {
|
||||
$crate::arch::x86_64::pti::unmap();
|
||||
|
||||
// Pop scratch registers, error code, and return
|
||||
fs_pop!();
|
||||
preserved_pop!();
|
||||
scratch_pop!();
|
||||
asm!("add rsp, 8" : : : : "intel", "volatile");
|
||||
interrupt_pop!();
|
||||
asm!("add rsp, 8" : : : : "intel", "volatile"); // pop error code
|
||||
iret!();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// It must create the IDT with the correct entries, those entries are
|
||||
/// defined in other files inside of the `arch` module
|
||||
|
||||
use core::slice;
|
||||
use core::{slice, mem, ptr};
|
||||
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
|
||||
use crate::allocator;
|
||||
@@ -17,6 +17,7 @@ use crate::gdt;
|
||||
use crate::idt;
|
||||
use crate::interrupt;
|
||||
use crate::log;
|
||||
use crate::macros::InterruptStack;
|
||||
use crate::memory;
|
||||
use crate::paging;
|
||||
|
||||
@@ -283,3 +284,31 @@ pub unsafe fn usermode(ip: usize, sp: usize, arg: usize) -> ! {
|
||||
: "intel", "volatile");
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[naked]
|
||||
pub unsafe fn usermode_interrupt_stack(stack: InterruptStack) -> ! {
|
||||
// Push fake stack to the actual stack
|
||||
let rsp: usize;
|
||||
asm!("sub rsp, $1" : "={rsp}"(rsp) : "r"(mem::size_of::<InterruptStack>()) : : "intel", "volatile");
|
||||
ptr::write(rsp as *mut InterruptStack, stack);
|
||||
|
||||
// Unmap kernel
|
||||
pti::unmap();
|
||||
|
||||
// Set up floating point and TLS
|
||||
asm!("mov ds, r14d
|
||||
mov es, r14d
|
||||
mov fs, r15d
|
||||
mov gs, r14d
|
||||
fninit"
|
||||
: // No output because it never returns
|
||||
: "{r14}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment
|
||||
"{r15}"(gdt::GDT_USER_TLS << 3 | 3) // TLS segment
|
||||
: "ds", "es", "fs", "gs"
|
||||
: "intel", "volatile");
|
||||
|
||||
// Go to usermode
|
||||
interrupt_pop!();
|
||||
iret!();
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
ptrace,
|
||||
scheme::{AtomicSchemeId, SchemeId},
|
||||
syscall::{
|
||||
data::{FloatRegisters, IntRegisters, PtraceEvent},
|
||||
data::{FloatRegisters, IntRegisters, PtraceEvent, Stat},
|
||||
error::*,
|
||||
flag::*,
|
||||
scheme::{calc_seek_offset_usize, Scheme},
|
||||
@@ -14,8 +14,9 @@ use crate::{
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
vec::Vec
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
cmp,
|
||||
@@ -91,6 +92,17 @@ enum Operation {
|
||||
Memory,
|
||||
Regs(RegsKind),
|
||||
Trace,
|
||||
Static(&'static str),
|
||||
}
|
||||
impl Operation {
|
||||
fn needs_child_process(self) -> bool {
|
||||
match self {
|
||||
Self::Memory => true,
|
||||
Self::Regs(_) => true,
|
||||
Self::Trace => true,
|
||||
Self::Static(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
struct MemData {
|
||||
offset: VirtualAddress,
|
||||
@@ -104,19 +116,25 @@ impl Default for MemData {
|
||||
struct TraceData {
|
||||
clones: Vec<ContextId>,
|
||||
}
|
||||
struct StaticData {
|
||||
buf: Box<[u8]>,
|
||||
offset: usize,
|
||||
}
|
||||
impl StaticData {
|
||||
fn new(buf: Box<[u8]>) -> Self {
|
||||
Self {
|
||||
buf,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
enum OperationData {
|
||||
Memory(MemData),
|
||||
Trace(TraceData),
|
||||
Static(StaticData),
|
||||
Other,
|
||||
}
|
||||
impl OperationData {
|
||||
fn default_for(op: Operation) -> OperationData {
|
||||
match op {
|
||||
Operation::Memory => OperationData::Memory(MemData::default()),
|
||||
Operation::Trace => OperationData::Trace(TraceData::default()),
|
||||
_ => OperationData::Other,
|
||||
}
|
||||
}
|
||||
fn trace_data(&mut self) -> Option<&mut TraceData> {
|
||||
match self {
|
||||
OperationData::Trace(data) => Some(data),
|
||||
@@ -129,6 +147,12 @@ impl OperationData {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn static_data(&mut self) -> Option<&mut StaticData> {
|
||||
match self {
|
||||
OperationData::Static(data) => Some(data),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -195,21 +219,31 @@ impl Scheme for ProcScheme {
|
||||
Some("regs/float") => Operation::Regs(RegsKind::Float),
|
||||
Some("regs/int") => Operation::Regs(RegsKind::Int),
|
||||
Some("trace") => Operation::Trace,
|
||||
Some("exe") => Operation::Static("exe"),
|
||||
_ => return Err(Error::new(EINVAL))
|
||||
};
|
||||
|
||||
let contexts = context::contexts();
|
||||
let target = contexts.get(pid).ok_or(Error::new(ESRCH))?;
|
||||
|
||||
let data;
|
||||
|
||||
{
|
||||
let target = target.read();
|
||||
|
||||
data = match operation {
|
||||
Operation::Memory => OperationData::Memory(MemData::default()),
|
||||
Operation::Trace => OperationData::Trace(TraceData::default()),
|
||||
Operation::Static(_) => OperationData::Static(StaticData::new(target.name.lock().clone())),
|
||||
_ => OperationData::Other,
|
||||
};
|
||||
|
||||
if let Status::Exited(_) = target.status {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
|
||||
// Unless root, check security
|
||||
if uid != 0 && gid != 0 {
|
||||
if operation.needs_child_process() && uid != 0 && gid != 0 {
|
||||
let current = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let current = current.read();
|
||||
|
||||
@@ -227,10 +261,10 @@ impl Scheme for ProcScheme {
|
||||
assert_eq!(id, current.id);
|
||||
assert_eq!(id, context.read().id);
|
||||
},
|
||||
None => return Err(Error::new(EPERM))
|
||||
None => return Err(Error::new(EPERM)),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
@@ -253,7 +287,7 @@ impl Scheme for ProcScheme {
|
||||
pid,
|
||||
operation,
|
||||
},
|
||||
data: OperationData::default_for(operation),
|
||||
data,
|
||||
});
|
||||
Ok(id)
|
||||
}
|
||||
@@ -304,6 +338,16 @@ impl Scheme for ProcScheme {
|
||||
};
|
||||
|
||||
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 => {
|
||||
// Won't context switch, don't worry about the locks
|
||||
let mut handles = self.handles.write();
|
||||
@@ -415,6 +459,7 @@ impl Scheme for ProcScheme {
|
||||
};
|
||||
|
||||
match info.operation {
|
||||
Operation::Static(_) => Err(Error::new(EBADF)),
|
||||
Operation::Memory => {
|
||||
// Won't context switch, don't worry about the locks
|
||||
let mut handles = self.handles.write();
|
||||
@@ -521,7 +566,7 @@ impl Scheme for ProcScheme {
|
||||
})?;
|
||||
|
||||
Ok(mem::size_of::<u64>())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,9 +585,12 @@ impl Scheme for ProcScheme {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
ptrace::Session::with_session(handle.info.pid, |session| {
|
||||
Ok(session.data.lock().session_fevent_flags())
|
||||
})
|
||||
match handle.info.operation {
|
||||
Operation::Trace => ptrace::Session::with_session(handle.info.pid, |session| {
|
||||
Ok(session.data.lock().session_fevent_flags())
|
||||
}),
|
||||
_ => Ok(EventFlags::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
@@ -553,7 +601,8 @@ impl Scheme for ProcScheme {
|
||||
Operation::Memory => "mem",
|
||||
Operation::Regs(RegsKind::Float) => "regs/float",
|
||||
Operation::Regs(RegsKind::Int) => "regs/int",
|
||||
Operation::Trace => "trace"
|
||||
Operation::Trace => "trace",
|
||||
Operation::Static(path) => path,
|
||||
});
|
||||
|
||||
let len = cmp::min(path.len(), buf.len());
|
||||
@@ -562,6 +611,27 @@ impl Scheme for ProcScheme {
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
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<usize> {
|
||||
let mut handle = self.handles.write().remove(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.continue_ignored_children();
|
||||
|
||||
@@ -6,6 +6,7 @@ use core::{intrinsics, mem};
|
||||
use core::ops::DerefMut;
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::arch::macros::InterruptStack;
|
||||
use crate::context::file::FileDescriptor;
|
||||
use crate::context::{ContextId, WaitpidKey};
|
||||
use crate::context;
|
||||
@@ -20,14 +21,14 @@ use crate::paging::temporary_page::TemporaryPage;
|
||||
use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE};
|
||||
use crate::{ptrace, syscall};
|
||||
use crate::scheme::FileHandle;
|
||||
use crate::start::usermode;
|
||||
use crate::start::{usermode, usermode_interrupt_stack};
|
||||
use crate::syscall::data::{SigAction, Stat};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{CloneFlags, CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND,
|
||||
CLONE_STACK, MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE,
|
||||
PTRACE_STOP_EXIT, SigActionFlags, SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK,
|
||||
SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED, wifcontinued,
|
||||
wifstopped};
|
||||
use crate::syscall::flag::{CloneFlags, CLONE_FILES, CLONE_FS, CLONE_SIGHAND,
|
||||
CLONE_STACK, CLONE_VFORK, CLONE_VM, MapFlags, PtraceFlags, PROT_EXEC,
|
||||
PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, PTRACE_STOP_EXIT, SigActionFlags,
|
||||
SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK, SIGCONT, SIGTERM, WaitFlags,
|
||||
WCONTINUED, WNOHANG,WUNTRACED, wifcontinued, wifstopped};
|
||||
use crate::syscall::ptrace_event;
|
||||
use crate::syscall::validate::{validate_slice, validate_slice_mut};
|
||||
|
||||
@@ -899,8 +900,22 @@ fn fexec_noreturn(
|
||||
}
|
||||
}
|
||||
|
||||
// Go to usermode
|
||||
unsafe { usermode(entry, sp, 0); }
|
||||
// Create dummy stack for ptrace to read from
|
||||
let mut regs = InterruptStack::new_usermode(entry, sp, 0);
|
||||
|
||||
// ptrace breakpoint
|
||||
let was_traced = {
|
||||
let _guard = ptrace::set_process_regs(&mut regs);
|
||||
ptrace::breakpoint_callback(PtraceFlags::PTRACE_STOP_EXEC, None).is_some()
|
||||
};
|
||||
|
||||
if !was_traced {
|
||||
// Go to usermode, fast route
|
||||
unsafe { usermode(entry, sp, 0) }
|
||||
} else {
|
||||
// Go to usermode, take ptrace-modified stack into account
|
||||
unsafe { usermode_interrupt_stack(regs) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option<Box<[u8]>>) -> Result<usize> {
|
||||
|
||||
Reference in New Issue
Block a user