@@ -12,20 +12,24 @@ use nix::{
12
12
unistd:: Pid ,
13
13
} ;
14
14
use std:: collections:: HashMap ;
15
+ use std:: path:: PathBuf ;
15
16
use tiny_nix_ipc:: Socket ;
17
+ use rand:: Rng ;
16
18
17
19
struct ChildInfo {
18
20
in_syscall : bool ,
21
+ in_spoiled : bool ,
22
+ spoiled_syscall : u64 ,
19
23
}
20
24
21
- fn decode_syscall_args ( regs : libc:: user_regs_struct ) -> RawSyscall {
25
+ fn decode_syscall_args ( regs : & libc:: user_regs_struct ) -> RawSyscall {
22
26
let mut out = RawSyscall {
23
27
syscall_id : 0 ,
24
28
args : [ 0 ; 6 ] ,
25
29
ret : 0 ,
26
30
} ;
27
31
out. ret = regs. rax ;
28
- out. syscall_id = regs. orig_rax ;
32
+ out. syscall_id = regs. orig_rax & 0xffffffu64 ;
29
33
out. args [ 0 ] = regs. rdi ;
30
34
out. args [ 1 ] = regs. rsi ;
31
35
out. args [ 2 ] = regs. rdx ;
@@ -35,6 +39,26 @@ fn decode_syscall_args(regs: libc::user_regs_struct) -> RawSyscall {
35
39
out
36
40
}
37
41
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
+
38
62
fn process_syscall (
39
63
raw : & RawSyscall ,
40
64
proc : Pid ,
@@ -125,7 +149,7 @@ pub(crate) unsafe fn parent(
125
149
)
126
150
. context ( "ptrace setoptions failed" ) ?;
127
151
128
- let new_child_info = ChildInfo { in_syscall : false } ;
152
+ let new_child_info = ChildInfo { in_syscall : false , in_spoiled : false , spoiled_syscall : 0 } ;
129
153
children. insert ( pid, new_child_info) ;
130
154
131
155
let event = Event {
@@ -148,19 +172,37 @@ pub(crate) unsafe fn parent(
148
172
( true , WaitStatus :: PtraceSyscall ( _) ) => {
149
173
let cur_info = children. get ( & pid) . unwrap ( ) ; // it's guaranteed here that get() returns Some
150
174
let started_syscall = !cur_info. in_syscall ;
175
+ let is_spoiled = cur_info. in_spoiled ;
176
+ let spoiled_syscall = cur_info. spoiled_syscall ;
151
177
let new_info = ChildInfo {
152
178
in_syscall : started_syscall,
179
+ in_spoiled : false ,
180
+ spoiled_syscall : 0 ,
153
181
} ;
154
182
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 ) )
156
184
. 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) ;
158
189
let def = magic. lookup_syscall_by_id ( SyscallId ( params. syscall_id as u32 ) ) ;
159
190
let child_pid = Pid :: from_raw ( pid as i32 ) ;
160
191
let mut decoded_params = match def {
161
192
Some ( def) => process_syscall ( & params, child_pid, magic, def) ,
162
193
None => None ,
163
194
} ;
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
+ }
164
206
decoded_params. as_mut ( ) . map ( |p| {
165
207
// attach backtrace if requested
166
208
if settings. capture_backtrace {
0 commit comments