@@ -5,7 +5,7 @@ use nix::sys::{ptrace, signal, wait};
55use  nix:: unistd; 
66
77use  super :: messages:: { Confirmation ,  MemEvents ,  TraceRequest } ; 
8- use  super :: { AccessEvent ,  FAKE_STACK_SIZE ,  StartFfiInfo } ; 
8+ use  super :: { AccessEvent ,  CALLBACK_STACK_SIZE ,  StartFfiInfo } ; 
99
1010/// The flags to use when calling `waitid()`. 
1111/// Since bitwise or on the nix version of these flags is implemented as a trait, 
@@ -263,7 +263,7 @@ pub fn sv_loop(
263263            ExecEvent :: Start ( ch_info)  => { 
264264                // All the pages that the child process is "allowed to" access. 
265265                ch_pages = ch_info. page_ptrs ; 
266-                 // And the fake  stack it allocated for us to use later. 
266+                 // And the temporary callback  stack it allocated for us to use later. 
267267                ch_stack = Some ( ch_info. stack_ptr ) ; 
268268
269269                // We received the signal and are no longer in the main listener loop, 
@@ -529,112 +529,113 @@ fn handle_segfault(
529529    let  addr = unsafe  {  siginfo. si_addr ( ) . addr ( )  } ; 
530530    let  page_addr = addr. strict_sub ( addr. strict_rem ( page_size) ) ; 
531531
532-     if  ch_pages. iter ( ) . any ( |pg| ( * pg..pg. strict_add ( page_size) ) . contains ( & addr) )  { 
533-         // Overall structure: 
534-         // - Get the address that caused the segfault 
535-         // - Unprotect the memory: we force the child to execute `mempr_off`, passing 
536-         //   parameters via global atomic variables. 
537-         // - Step 1 instruction 
538-         // - Parse executed code to estimate size & type of access 
539-         // - Reprotect the memory by executing `mempr_on` in the child. 
540-         // - Continue 
541- 
542-         // Ensure the stack is properly zeroed out! 
543-         for  a in  ( ch_stack..ch_stack. strict_add ( FAKE_STACK_SIZE ) ) . step_by ( ARCH_WORD_SIZE )  { 
544-             ptrace:: write ( pid,  std:: ptr:: with_exposed_provenance_mut ( a) ,  0 ) . unwrap ( ) ; 
545-         } 
546- 
547-         // Guard against both architectures with upwards and downwards-growing stacks. 
548-         let  stack_ptr = ch_stack. strict_add ( FAKE_STACK_SIZE  / 2 ) ; 
549-         let  regs_bak = ptrace:: getregs ( pid) . unwrap ( ) ; 
550-         let  mut  new_regs = regs_bak; 
551-         let  ip_prestep = regs_bak. ip ( ) ; 
552- 
553-         // Move the instr ptr into the deprotection code. 
554-         #[ expect( clippy:: as_conversions) ]  
555-         new_regs. set_ip ( mempr_off as  usize ) ; 
556-         // Don't mess up the stack by accident! 
557-         new_regs. set_sp ( stack_ptr) ; 
558- 
559-         // Modify the PAGE_ADDR global on the child process to point to the page 
560-         // that we want unprotected. 
561-         ptrace:: write ( 
562-             pid, 
563-             ( & raw  const  PAGE_ADDR ) . cast_mut ( ) . cast ( ) , 
564-             libc:: c_long:: try_from ( page_addr) . unwrap ( ) , 
565-         ) 
566-         . unwrap ( ) ; 
567- 
568-         // Check if we also own the next page, and if so unprotect it in case 
569-         // the access spans the page boundary. 
570-         let  flag = if  ch_pages. contains ( & page_addr. strict_add ( page_size) )  {  2  }  else  {  1  } ; 
571-         ptrace:: write ( pid,  ( & raw  const  PAGE_COUNT ) . cast_mut ( ) . cast ( ) ,  flag) . unwrap ( ) ; 
572- 
573-         ptrace:: setregs ( pid,  new_regs) . unwrap ( ) ; 
574- 
575-         // Our mempr_* functions end with a raise(SIGSTOP). 
576-         wait_for_signal ( Some ( pid) ,  signal:: SIGSTOP ,  true ) ?; 
577- 
578-         // Step 1 instruction. 
579-         ptrace:: setregs ( pid,  regs_bak) . unwrap ( ) ; 
580-         ptrace:: step ( pid,  None ) . unwrap ( ) ; 
581-         // Don't use wait_for_signal here since 1 instruction doesn't give room 
582-         // for any uncertainty + we don't want it `cont()`ing randomly by accident 
583-         // Also, don't let it continue with unprotected memory if something errors! 
584-         let  _ = wait:: waitid ( wait:: Id :: Pid ( pid) ,  WAIT_FLAGS ) . map_err ( |_| ExecEnd ( None ) ) ?; 
585- 
586-         // Zero out again to be safe 
587-         for  a in  ( ch_stack..ch_stack. strict_add ( FAKE_STACK_SIZE ) ) . step_by ( ARCH_WORD_SIZE )  { 
588-             ptrace:: write ( pid,  std:: ptr:: with_exposed_provenance_mut ( a) ,  0 ) . unwrap ( ) ; 
589-         } 
590- 
591-         // Save registers and grab the bytes that were executed. This would 
592-         // be really nasty if it was a jump or similar but those thankfully 
593-         // won't do memory accesses and so can't trigger this! 
594-         let  regs_bak = ptrace:: getregs ( pid) . unwrap ( ) ; 
595-         new_regs = regs_bak; 
596-         let  ip_poststep = regs_bak. ip ( ) ; 
597-         // We need to do reads/writes in word-sized chunks. 
598-         let  diff = ( ip_poststep. strict_sub ( ip_prestep) ) . div_ceil ( ARCH_WORD_SIZE ) ; 
599-         let  instr = ( ip_prestep..ip_prestep. strict_add ( diff) ) . fold ( vec ! [ ] ,  |mut  ret,  ip| { 
600-             // This only needs to be a valid pointer in the child process, not ours. 
601-             ret. append ( 
602-                 & mut  ptrace:: read ( pid,  std:: ptr:: without_provenance_mut ( ip) ) 
603-                     . unwrap ( ) 
604-                     . to_ne_bytes ( ) 
605-                     . to_vec ( ) , 
606-             ) ; 
607-             ret
608-         } ) ; 
609- 
610-         // Now figure out the size + type of access and log it down. 
611-         // This will mark down e.g. the same area being read multiple times, 
612-         // since it's more efficient to compress the accesses at the end. 
613-         if  capstone_disassemble ( & instr,  addr,  cs,  acc_events) . is_err ( )  { 
614-             // Read goes first because we need to be pessimistic. 
615-             acc_events. push ( AccessEvent :: Read ( addr..addr. strict_add ( ARCH_MAX_ACCESS_SIZE ) ) ) ; 
616-             acc_events. push ( AccessEvent :: Write ( addr..addr. strict_add ( ARCH_MAX_ACCESS_SIZE ) ) ) ; 
617-         } 
618- 
619-         // Reprotect everything and continue. 
620-         #[ expect( clippy:: as_conversions) ]  
621-         new_regs. set_ip ( mempr_on as  usize ) ; 
622-         new_regs. set_sp ( stack_ptr) ; 
623-         ptrace:: setregs ( pid,  new_regs) . unwrap ( ) ; 
624-         wait_for_signal ( Some ( pid) ,  signal:: SIGSTOP ,  true ) ?; 
625- 
626-         ptrace:: setregs ( pid,  regs_bak) . unwrap ( ) ; 
627-         ptrace:: syscall ( pid,  None ) . unwrap ( ) ; 
628-         Ok ( ( ) ) 
629-     }  else  { 
630-         // This was a real segfault, so print some debug info and quit. 
532+     if  !ch_pages. iter ( ) . any ( |pg| ( * pg..pg. strict_add ( page_size) ) . contains ( & addr) )  { 
533+         // This was a real segfault (not one of the Miri memory pages), so print some debug info and 
534+         // quit. 
631535        let  regs = ptrace:: getregs ( pid) . unwrap ( ) ; 
632536        eprintln ! ( "Segfault occurred during FFI at {addr:#018x}" ) ; 
633537        eprintln ! ( "Expected access on pages: {ch_pages:#018x?}" ) ; 
634538        eprintln ! ( "Register dump: {regs:#x?}" ) ; 
635539        ptrace:: kill ( pid) . unwrap ( ) ; 
636-         Err ( ExecEnd ( None ) ) 
540+         return   Err ( ExecEnd ( None ) ) ; 
637541    } 
542+ 
543+     // Overall structure: 
544+     // - Get the address that caused the segfault 
545+     // - Unprotect the memory: we force the child to execute `mempr_off`, passing parameters via 
546+     //   global atomic variables. This is what we use the temporary callback stack for. 
547+     // - Step 1 instruction 
548+     // - Parse executed code to estimate size & type of access 
549+     // - Reprotect the memory by executing `mempr_on` in the child. 
550+     // - Continue 
551+ 
552+     // Ensure the stack is properly zeroed out! 
553+     for  a in  ( ch_stack..ch_stack. strict_add ( CALLBACK_STACK_SIZE ) ) . step_by ( ARCH_WORD_SIZE )  { 
554+         ptrace:: write ( pid,  std:: ptr:: with_exposed_provenance_mut ( a) ,  0 ) . unwrap ( ) ; 
555+     } 
556+ 
557+     // Guard against both architectures with upwards and downwards-growing stacks. 
558+     let  stack_ptr = ch_stack. strict_add ( CALLBACK_STACK_SIZE  / 2 ) ; 
559+     let  regs_bak = ptrace:: getregs ( pid) . unwrap ( ) ; 
560+     let  mut  new_regs = regs_bak; 
561+     let  ip_prestep = regs_bak. ip ( ) ; 
562+ 
563+     // Move the instr ptr into the deprotection code. 
564+     #[ expect( clippy:: as_conversions) ]  
565+     new_regs. set_ip ( mempr_off as  usize ) ; 
566+     // Don't mess up the stack by accident! 
567+     new_regs. set_sp ( stack_ptr) ; 
568+ 
569+     // Modify the PAGE_ADDR global on the child process to point to the page 
570+     // that we want unprotected. 
571+     ptrace:: write ( 
572+         pid, 
573+         ( & raw  const  PAGE_ADDR ) . cast_mut ( ) . cast ( ) , 
574+         libc:: c_long:: try_from ( page_addr) . unwrap ( ) , 
575+     ) 
576+     . unwrap ( ) ; 
577+ 
578+     // Check if we also own the next page, and if so unprotect it in case 
579+     // the access spans the page boundary. 
580+     let  flag = if  ch_pages. contains ( & page_addr. strict_add ( page_size) )  {  2  }  else  {  1  } ; 
581+     ptrace:: write ( pid,  ( & raw  const  PAGE_COUNT ) . cast_mut ( ) . cast ( ) ,  flag) . unwrap ( ) ; 
582+ 
583+     ptrace:: setregs ( pid,  new_regs) . unwrap ( ) ; 
584+ 
585+     // Our mempr_* functions end with a raise(SIGSTOP). 
586+     wait_for_signal ( Some ( pid) ,  signal:: SIGSTOP ,  true ) ?; 
587+ 
588+     // Step 1 instruction. 
589+     ptrace:: setregs ( pid,  regs_bak) . unwrap ( ) ; 
590+     ptrace:: step ( pid,  None ) . unwrap ( ) ; 
591+     // Don't use wait_for_signal here since 1 instruction doesn't give room 
592+     // for any uncertainty + we don't want it `cont()`ing randomly by accident 
593+     // Also, don't let it continue with unprotected memory if something errors! 
594+     let  _ = wait:: waitid ( wait:: Id :: Pid ( pid) ,  WAIT_FLAGS ) . map_err ( |_| ExecEnd ( None ) ) ?; 
595+ 
596+     // Zero out again to be safe 
597+     for  a in  ( ch_stack..ch_stack. strict_add ( CALLBACK_STACK_SIZE ) ) . step_by ( ARCH_WORD_SIZE )  { 
598+         ptrace:: write ( pid,  std:: ptr:: with_exposed_provenance_mut ( a) ,  0 ) . unwrap ( ) ; 
599+     } 
600+ 
601+     // Save registers and grab the bytes that were executed. This would 
602+     // be really nasty if it was a jump or similar but those thankfully 
603+     // won't do memory accesses and so can't trigger this! 
604+     let  regs_bak = ptrace:: getregs ( pid) . unwrap ( ) ; 
605+     new_regs = regs_bak; 
606+     let  ip_poststep = regs_bak. ip ( ) ; 
607+     // We need to do reads/writes in word-sized chunks. 
608+     let  diff = ( ip_poststep. strict_sub ( ip_prestep) ) . div_ceil ( ARCH_WORD_SIZE ) ; 
609+     let  instr = ( ip_prestep..ip_prestep. strict_add ( diff) ) . fold ( vec ! [ ] ,  |mut  ret,  ip| { 
610+         // This only needs to be a valid pointer in the child process, not ours. 
611+         ret. append ( 
612+             & mut  ptrace:: read ( pid,  std:: ptr:: without_provenance_mut ( ip) ) 
613+                 . unwrap ( ) 
614+                 . to_ne_bytes ( ) 
615+                 . to_vec ( ) , 
616+         ) ; 
617+         ret
618+     } ) ; 
619+ 
620+     // Now figure out the size + type of access and log it down. 
621+     // This will mark down e.g. the same area being read multiple times, 
622+     // since it's more efficient to compress the accesses at the end. 
623+     if  capstone_disassemble ( & instr,  addr,  cs,  acc_events) . is_err ( )  { 
624+         // Read goes first because we need to be pessimistic. 
625+         acc_events. push ( AccessEvent :: Read ( addr..addr. strict_add ( ARCH_MAX_ACCESS_SIZE ) ) ) ; 
626+         acc_events. push ( AccessEvent :: Write ( addr..addr. strict_add ( ARCH_MAX_ACCESS_SIZE ) ) ) ; 
627+     } 
628+ 
629+     // Reprotect everything and continue. 
630+     #[ expect( clippy:: as_conversions) ]  
631+     new_regs. set_ip ( mempr_on as  usize ) ; 
632+     new_regs. set_sp ( stack_ptr) ; 
633+     ptrace:: setregs ( pid,  new_regs) . unwrap ( ) ; 
634+     wait_for_signal ( Some ( pid) ,  signal:: SIGSTOP ,  true ) ?; 
635+ 
636+     ptrace:: setregs ( pid,  regs_bak) . unwrap ( ) ; 
637+     ptrace:: syscall ( pid,  None ) . unwrap ( ) ; 
638+     Ok ( ( ) ) 
638639} 
639640
640641// We only get dropped into these functions via offsetting the instr pointer 
0 commit comments