Verified Commit 1f47418f authored by jD91mZM2's avatar jD91mZM2
Browse files

WIP(ptrace): Non-blocking tests

parent b356ddc3
......@@ -4,7 +4,7 @@
name = "acid"
version = "0.1.0"
dependencies = [
"redox_syscall 0.1.54 (git+https://gitlab.redox-os.org/jD91mZM2/syscall.git?branch=ptrace)",
"redox_syscall 0.1.54",
"x86 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -205,7 +205,6 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.1.54"
source = "git+https://gitlab.redox-os.org/jD91mZM2/syscall.git?branch=ptrace#a7aff4b60d537e6f336ff7e8affe8801a92c20f0"
[[package]]
name = "rustc-serialize"
......@@ -290,7 +289,6 @@ dependencies = [
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
"checksum raw-cpuid 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13b844e4049605ff38fed943f5c7b2c691fad68d9d5bf074d2720554c4e48246"
"checksum redox_syscall 0.1.54 (git+https://gitlab.redox-os.org/jD91mZM2/syscall.git?branch=ptrace)" = "<none>"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97b18e9e53de541f11e497357d6c5eaeb39f0cb9c8734e274abe4935f6991fa"
"checksum serde_json 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5aaee47e038bf9552d30380d3973fff2593ee0a76d81ad4c581f267cdcadf36"
......
......@@ -5,4 +5,5 @@ authors = ["Jeremy Soller <jackpot51@gmail.com>"]
[dependencies]
x86 = "0.7"
redox_syscall = { git = "https://gitlab.redox-os.org/jD91mZM2/syscall.git", branch = "ptrace" }
# redox_syscall = { git = "https://gitlab.redox-os.org/jD91mZM2/syscall.git", branch = "ptrace" }
redox_syscall = { path = "/home/user/redox-nix/redox/kernel/syscall" }
......@@ -62,6 +62,7 @@ fn ptrace() -> Result<(), String> {
if pid == 0 {
unsafe {
asm!("
// Wait until tracer is started
mov rax, 20 // GETPID
syscall
......@@ -71,7 +72,9 @@ fn ptrace() -> Result<(), String> {
mov rsi, 19 // SIGSTOP
syscall
// Start of body
// Start of body:
// Test basic singlestepping
mov rax, 1
push rax
mov rax, 2
......@@ -79,43 +82,80 @@ fn ptrace() -> Result<(), String> {
mov rax, 3
pop rax
pop rax
// End of body
// Test behavior if tracer aborts a breakpoint before it's reached
call wait_for_a_while
mov rax, 158 // SYS_YIELD
syscall
mov rax, 37 // SYS_KILL
mov rsi, 19 // SIGSTOP
syscall
// Test nonblock & sysemu
call wait_for_a_while
mov rax, 20 // GETPID
syscall
mov rdi, rax
mov rax, 1 // SYS_EXIT
mov rdi, 0
syscall
// Without a jump, this code is unreachable. Therefore function definitions go here.
wait_for_a_while:
mov rax, 4294967295
.loop:
sub rax, 1
jne .loop
ret
"
: : : : "intel", "volatile"
);
}
}
// Wait until child is ready to be traced
println!("Waiting until child is ready to be traced...");
let mut status = 0;
syscall::waitpid(pid, &mut status, syscall::WUNTRACED).map_err(|e| format!("waitpid failed: {}", e))?;
// Stop & attach process + get handle to registers
let mut proc_file = File::open(format!("proc:{}/trace", pid)).map_err(|e| format!("open failed: {}", e))?;
let mut regs_file = unsafe {
println!("Done! Attaching tracer...");
// Stop and attach process + get handle to registers
let proc_file = File::open(format!("proc:{}/trace", pid)).map_err(|e| format!("open failed: {}", e))?;
let regs_file = unsafe {
File::from_raw_fd(
syscall::dup(proc_file.as_raw_fd() as usize, b"regs/int")
.map_err(|e| format!("dup failed: {}", e))? as c_int
)
};
// Schedule restart of process when resumed
println!("Schedule restart of process when resumed...");
syscall::kill(pid, syscall::SIGCONT).map_err(|e| format!("kill failed: {}", e))?;
let mut next = move |op| -> Result<syscall::IntRegisters, String> {
proc_file.write(&[op]).map_err(|e| format!("ptrace operation failed: {}", e))?;
let getregs = || -> Result<syscall::IntRegisters, String> {
let mut regs: syscall::IntRegisters = syscall::IntRegisters::default();
regs_file.read(&mut regs).map_err(|e| format!("reading registers failed: {}", e))?;
(&regs_file).read(&mut regs).map_err(|e| format!("reading registers failed: {}", e))?;
Ok(regs)
};
// Step out of syscall down to the next instruction
let setregs = |regs: &syscall::IntRegisters| -> Result<(), String> {
(&regs_file).write(&regs).map_err(|e| format!("writing registers failed: {}", e))?;
Ok(())
};
let next = |op| -> Result<syscall::IntRegisters, String> {
(&proc_file).write(&[op]).map_err(|e| format!("ptrace operation failed: {}", e))?;
getregs()
};
println!("Stepping away from the syscall instruction...");
let _ = next(syscall::PTRACE_SINGLESTEP)?;
println!("Testing basic singlestepping...");
assert_eq!(next(syscall::PTRACE_SINGLESTEP)?.rax, 1);
assert_eq!(next(syscall::PTRACE_SINGLESTEP)?.rax, 2);
assert_eq!(next(syscall::PTRACE_SINGLESTEP)?.rax, 2);
......@@ -123,7 +163,47 @@ fn ptrace() -> Result<(), String> {
assert_eq!(next(syscall::PTRACE_SINGLESTEP)?.rax, 2);
assert_eq!(next(syscall::PTRACE_SINGLESTEP)?.rax, 1);
assert_eq!(next(syscall::PTRACE_SYSCALL)?.rax, syscall::SYS_EXIT);
let old_flags = syscall::fcntl(proc_file.as_raw_fd() as usize, syscall::F_GETFL, 0)
.map_err(|e| format!("fcntl get failed: {}", e))?;
let new_flags = old_flags | syscall::O_NONBLOCK;
syscall::fcntl(proc_file.as_raw_fd() as usize, syscall::F_SETFL, new_flags)
.map_err(|e| format!("fcntl set failed: {}", e))?;
println!("Testing behavior of obsolete breakpoints...");
next(syscall::PTRACE_SYSCALL)?;
next(syscall::PTRACE_CONT)?;
println!("Tracee RAX: {}", getregs()?.rax);
println!("Waiting for next signal from tracee that it's ready to be traced again...");
syscall::waitpid(pid, &mut status, syscall::WUNTRACED).map_err(|e| format!("waitpid failed: {}", e))?;
println!("Setting sysemu breakpoint...");
next(syscall::PTRACE_SYSCALL | syscall::PTRACE_SYSEMU)?;
println!("Schedule restart of process after breakpoint is set...");
syscall::kill(pid, syscall::SIGCONT).map_err(|e| format!("kill failed: {}", e))?;
println!("After non-blocking ptrace, execution continues as normal:");
for _ in 0..5 {
println!("Tracee RAX: {}", getregs()?.rax);
}
println!("Overriding GETPID call...");
let mut regs = next(syscall::PTRACE_WAIT)?;
assert_eq!(regs.rax, syscall::SYS_GETPID);
regs.rax = 123;
setregs(&regs)?;
syscall::fcntl(proc_file.as_raw_fd() as usize, syscall::F_SETFL, old_flags)
.map_err(|e| format!("fcntl set failed: {}", e))?;
println!("Checking exit status...");
let regs = next(syscall::PTRACE_SYSCALL)?;
assert_eq!(regs.rax, syscall::SYS_EXIT);
assert_eq!(regs.rdi, 123);
assert_eq!((&proc_file).write(&[syscall::PTRACE_SYSCALL]).unwrap_err().raw_os_error(), Some(syscall::ESRCH));
println!("All done and tested!");
Ok(())
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment