@@ -12,20 +12,24 @@ use nix::{
1212 unistd:: Pid ,
1313} ;
1414use std:: collections:: HashMap ;
15+ use std:: path:: PathBuf ;
1516use tiny_nix_ipc:: Socket ;
17+ use rand:: Rng ;
1618
1719struct ChildInfo {
1820 in_syscall : bool ,
21+ in_spoiled : bool ,
22+ spoiled_syscall : u64 ,
1923}
2024
21- fn decode_syscall_args ( regs : libc:: user_regs_struct ) -> RawSyscall {
25+ fn decode_syscall_args ( regs : & libc:: user_regs_struct ) -> RawSyscall {
2226 let mut out = RawSyscall {
2327 syscall_id : 0 ,
2428 args : [ 0 ; 6 ] ,
2529 ret : 0 ,
2630 } ;
2731 out. ret = regs. rax ;
28- out. syscall_id = regs. orig_rax ;
32+ out. syscall_id = regs. orig_rax & 0xffffffu64 ;
2933 out. args [ 0 ] = regs. rdi ;
3034 out. args [ 1 ] = regs. rsi ;
3135 out. args [ 2 ] = regs. rdx ;
@@ -35,6 +39,26 @@ fn decode_syscall_args(regs: libc::user_regs_struct) -> RawSyscall {
3539 out
3640}
3741
42+ fn spoil ( pid : Pid , children : & mut HashMap < u32 , ChildInfo > , regs : libc:: user_regs_struct ) -> Result < ( ) , nix:: Error > {
43+ let ci1 = children. get ( & ( pid. as_raw ( ) as u32 ) ) . unwrap ( ) ;
44+ let ci2 = ChildInfo {
45+ in_syscall : ci1. in_syscall ,
46+ in_spoiled : true ,
47+ spoiled_syscall : regs. orig_rax ,
48+ } ;
49+ children. insert ( pid. as_raw ( ) as u32 , ci2) ;
50+ let mut regs = regs;
51+ regs. rax = 39 /*getpid*/ ;
52+ regs. orig_rax = 39 /*getpid*/ ;
53+ nix:: sys:: ptrace:: setregs ( pid, regs)
54+ }
55+
56+ fn return_eio ( pid : Pid , regs : & mut libc:: user_regs_struct , syscall : u64 ) -> Result < ( ) , nix:: Error > {
57+ regs. orig_rax = syscall;
58+ regs. rax = -( nix:: errno:: Errno :: EIO as i32 as i64 ) as u64 ;
59+ nix:: sys:: ptrace:: setregs ( pid, * regs)
60+ }
61+
3862fn process_syscall (
3963 raw : & RawSyscall ,
4064 proc : Pid ,
@@ -125,7 +149,7 @@ pub(crate) unsafe fn parent(
125149 )
126150 . context ( "ptrace setoptions failed" ) ?;
127151
128- let new_child_info = ChildInfo { in_syscall : false } ;
152+ let new_child_info = ChildInfo { in_syscall : false , in_spoiled : false , spoiled_syscall : 0 } ;
129153 children. insert ( pid, new_child_info) ;
130154
131155 let event = Event {
@@ -148,19 +172,37 @@ pub(crate) unsafe fn parent(
148172 ( true , WaitStatus :: PtraceSyscall ( _) ) => {
149173 let cur_info = children. get ( & pid) . unwrap ( ) ; // it's guaranteed here that get() returns Some
150174 let started_syscall = !cur_info. in_syscall ;
175+ let is_spoiled = cur_info. in_spoiled ;
176+ let spoiled_syscall = cur_info. spoiled_syscall ;
151177 let new_info = ChildInfo {
152178 in_syscall : started_syscall,
179+ in_spoiled : false ,
180+ spoiled_syscall : 0 ,
153181 } ;
154182 children. insert ( pid, new_info) ;
155- let regs = nix:: sys:: ptrace:: getregs ( Pid :: from_raw ( pid as i32 ) )
183+ let mut regs = nix:: sys:: ptrace:: getregs ( Pid :: from_raw ( pid as i32 ) )
156184 . context ( "ptrace getregs failed" ) ?;
157- let params = decode_syscall_args ( regs) ;
185+ if is_spoiled {
186+ return_eio ( Pid :: from_raw ( pid as i32 ) , & mut regs, spoiled_syscall) . context ( "return -EIO failed" ) ?;
187+ }
188+ let params = decode_syscall_args ( & regs) ;
158189 let def = magic. lookup_syscall_by_id ( SyscallId ( params. syscall_id as u32 ) ) ;
159190 let child_pid = Pid :: from_raw ( pid as i32 ) ;
160191 let mut decoded_params = match def {
161192 Some ( def) => process_syscall ( & params, child_pid, magic, def) ,
162193 None => None ,
163194 } ;
195+ if started_syscall && decoded_params. is_some ( ) && ( params. syscall_id == 2 /*open*/ || params. syscall_id == 257 /*openat*/ ) { //TODO: x86_64 only
196+ let decoded_params = decoded_params. as_ref ( ) . unwrap ( ) ;
197+ let arg_id = if params. syscall_id == 2 { 0 } else { 1 } ;
198+ let path: PathBuf = match & decoded_params. args [ arg_id] {
199+ crate :: magic:: ty:: Value :: String ( s) => s,
200+ _ => panic ! ( "open/openat with non-string argument" )
201+ } . into ( ) ;
202+ if settings. fail_path . is_some ( ) && path. starts_with ( settings. fail_path . as_ref ( ) . unwrap ( ) ) && rand:: thread_rng ( ) . gen_range ( 0 , 100 ) == 0 {
203+ spoil ( Pid :: from_raw ( pid as i32 ) , & mut children, regs) . context ( "spoil failed" ) ?;
204+ }
205+ }
164206 decoded_params. as_mut ( ) . map ( |p| {
165207 // attach backtrace if requested
166208 if settings. capture_backtrace {
0 commit comments