@@ -13,8 +13,6 @@ use nix::unistd::getpid;
1313#[ cfg( linux_android) ]
1414use std:: mem;
1515
16- use crate :: * ;
17-
1816#[ test]
1917fn test_ptrace ( ) {
2018 // Just make sure ptrace can be called at all, for now.
@@ -410,3 +408,73 @@ fn test_ptrace_syscall_info() {
410408 } ,
411409 }
412410}
411+
412+ #[ cfg( all( target_os = "linux" , target_env = "gnu" ) ) ]
413+ #[ test]
414+ fn test_ptrace_set_syscall_info ( ) {
415+ use nix:: sys:: mman:: { mmap_anonymous, MapFlags , ProtFlags } ;
416+ use nix:: sys:: ptrace;
417+ use nix:: sys:: signal:: { raise, Signal :: * } ;
418+ use nix:: sys:: wait:: { waitpid, WaitStatus } ;
419+ use nix:: unistd:: { fork, ForkResult :: * } ;
420+ use std:: num:: NonZero ;
421+
422+ require_capability ! ( "test_ptrace_set_syscall_info" , CAP_SYS_PTRACE ) ;
423+
424+ let _m = crate :: FORK_MTX . lock ( ) ;
425+ match unsafe { fork ( ) } . expect ( "Error: Fork Failed" ) {
426+ Child => {
427+ ptrace:: traceme ( ) . unwrap ( ) ;
428+ raise ( SIGSTOP ) . unwrap ( ) ;
429+
430+ unsafe {
431+ let my_memory = mmap_anonymous (
432+ None ,
433+ NonZero :: new ( 1 ) . unwrap ( ) ,
434+ ProtFlags :: PROT_WRITE ,
435+ MapFlags :: MAP_PRIVATE ,
436+ )
437+ . unwrap_or_else ( |_| :: libc:: _exit ( 1 ) )
438+ . cast :: < u8 > ( ) ;
439+
440+ * my_memory. as_ptr ( ) = 42 ;
441+ :: libc:: _exit ( 0 ) ;
442+ }
443+ }
444+ Parent { child } => {
445+ assert_eq ! (
446+ waitpid( child, None ) ,
447+ Ok ( WaitStatus :: Stopped ( child, SIGSTOP ) )
448+ ) ;
449+ ptrace:: setoptions ( child, Options :: PTRACE_O_TRACESYSGOOD ) . unwrap ( ) ;
450+ ptrace:: syscall ( child, None ) . unwrap ( ) ;
451+
452+ // Hijack the syscall and remove PROT_WRITE to force a SEGFAULT in the child
453+ assert_eq ! (
454+ waitpid( child, None ) ,
455+ Ok ( WaitStatus :: PtraceSyscall ( child) )
456+ ) ;
457+ let mut si = ptrace:: syscall_info ( child) . unwrap ( ) ;
458+ assert_eq ! ( si. op, libc:: PTRACE_SYSCALL_INFO_ENTRY ) ;
459+ unsafe {
460+ si. u . entry . args [ 2 ] = ProtFlags :: PROT_NONE . bits ( ) as u64 ;
461+ }
462+ let set_syscall_res = ptrace:: set_syscall_info ( child, & si) ;
463+ ptrace:: cont ( child, None ) . unwrap ( ) ;
464+
465+ if set_syscall_res. is_err ( ) {
466+ assert_eq ! (
467+ waitpid( child, None ) ,
468+ Ok ( WaitStatus :: Exited ( child, 0 ) )
469+ ) ;
470+ crate :: skip!( "PTRACE_SET_SYSCALL_INFO failed: Linux >= 6.16 is required, skipping test." ) ;
471+ }
472+
473+ assert_eq ! (
474+ waitpid( child, None ) ,
475+ Ok ( WaitStatus :: Stopped ( child, SIGSEGV ) )
476+ ) ;
477+ ptrace:: detach ( child, SIGSEGV ) . unwrap ( ) ;
478+ }
479+ }
480+ }
0 commit comments