@@ -17,7 +17,7 @@ limitations under the License.
1717use std:: fmt;
1818use std:: fmt:: { Debug , Formatter } ;
1919use std:: string:: String ;
20- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
20+ use std:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
2121use std:: sync:: { Arc , Mutex } ;
2222
2323use log:: LevelFilter ;
@@ -327,10 +327,11 @@ impl HypervWindowsDriver {
327327 } ;
328328
329329 let interrupt_handle = Arc :: new ( WindowsInterruptHandle {
330- running : AtomicBool :: new ( false ) ,
331- cancel_requested : AtomicBool :: new ( false ) ,
330+ running : AtomicU64 :: new ( 0 ) ,
331+ cancel_requested : AtomicU64 :: new ( 0 ) ,
332332 #[ cfg( gdb) ]
333333 debug_interrupt : AtomicBool :: new ( false ) ,
334+ call_active : AtomicBool :: new ( false ) ,
334335 partition_handle,
335336 dropped : AtomicBool :: new ( false ) ,
336337 } ) ;
@@ -549,7 +550,8 @@ impl Hypervisor for HypervWindowsDriver {
549550 & mut self ,
550551 #[ cfg( feature = "trace_guest" ) ] tc : & mut crate :: sandbox:: trace:: TraceContext ,
551552 ) -> Result < super :: HyperlightExit > {
552- self . interrupt_handle . running . store ( true , Ordering :: Relaxed ) ;
553+ // Get current generation and set running bit
554+ let generation = self . interrupt_handle . set_running_bit ( ) ;
553555
554556 #[ cfg( not( gdb) ) ]
555557 let debug_interrupt = false ;
@@ -559,11 +561,10 @@ impl Hypervisor for HypervWindowsDriver {
559561 . debug_interrupt
560562 . load ( Ordering :: Relaxed ) ;
561563
562- // Don't run the vcpu if `cancel_requested` is true
564+ // Check if cancellation was requested for THIS generation
563565 let exit_context = if self
564566 . interrupt_handle
565- . cancel_requested
566- . load ( Ordering :: Relaxed )
567+ . is_cancel_requested_for_generation ( generation)
567568 || debug_interrupt
568569 {
569570 WHV_RUN_VP_EXIT_CONTEXT {
@@ -578,12 +579,21 @@ impl Hypervisor for HypervWindowsDriver {
578579
579580 self . processor . run ( ) ?
580581 } ;
581- self . interrupt_handle
582- . cancel_requested
583- . store ( false , Ordering :: Relaxed ) ;
584- self . interrupt_handle
585- . running
586- . store ( false , Ordering :: Relaxed ) ;
582+
583+ // Clear running bit
584+ self . interrupt_handle . clear_running_bit ( ) ;
585+
586+ let is_canceled = exit_context. ExitReason == WHV_RUN_VP_EXIT_REASON ( 8193i32 ) ; // WHvRunVpExitReasonCanceled
587+
588+ // Check if this was a manual cancellation (vs internal Windows cancellation)
589+ let cancel_was_requested_manually = self
590+ . interrupt_handle
591+ . is_cancel_requested_for_generation ( generation) ;
592+
593+ // Only clear cancel_requested if we're actually processing a cancellation for this generation
594+ if is_canceled && cancel_was_requested_manually {
595+ self . interrupt_handle . clear_cancel_requested ( ) ;
596+ }
587597
588598 #[ cfg( gdb) ]
589599 let debug_interrupt = self
@@ -659,12 +669,32 @@ impl Hypervisor for HypervWindowsDriver {
659669 // return a special exit reason so that the gdb thread can handle it
660670 // and resume execution
661671 HyperlightExit :: Debug ( VcpuStopReason :: Interrupt )
672+ } else if !cancel_was_requested_manually {
673+ // This was an internal cancellation
674+ // The virtualization stack can use this function to return the control
675+ // of a virtual processor back to the virtualization stack in case it
676+ // needs to change the state of a VM or to inject an event into the processor
677+ // see https://learn.microsoft.com/en-us/virtualization/api/hypervisor-platform/funcs/whvcancelrunvirtualprocessor#remarks
678+ debug ! ( "Internal cancellation detected, returning Retry error" ) ;
679+ HyperlightExit :: Retry ( )
662680 } else {
663681 HyperlightExit :: Cancelled ( )
664682 }
665683
666684 #[ cfg( not( gdb) ) ]
667- HyperlightExit :: Cancelled ( )
685+ {
686+ if !cancel_was_requested_manually {
687+ // This was an internal cancellation
688+ // The virtualization stack can use this function to return the control
689+ // of a virtual processor back to the virtualization stack in case it
690+ // needs to change the state of a VM or to inject an event into the processor
691+ // see https://learn.microsoft.com/en-us/virtualization/api/hypervisor-platform/funcs/whvcancelrunvirtualprocessor#remarks
692+ debug ! ( "Internal cancellation detected, returning Retry error" ) ;
693+ HyperlightExit :: Retry ( )
694+ } else {
695+ HyperlightExit :: Cancelled ( )
696+ }
697+ }
668698 }
669699 #[ cfg( gdb) ]
670700 WHV_RUN_VP_EXIT_REASON ( 4098i32 ) => {
@@ -964,30 +994,77 @@ impl Drop for HypervWindowsDriver {
964994
965995#[ derive( Debug ) ]
966996pub struct WindowsInterruptHandle {
967- // `WHvCancelRunVirtualProcessor()` will return Ok even if the vcpu is not running, which is the reason we need this flag.
968- running : AtomicBool ,
969- cancel_requested : AtomicBool ,
997+ /// Combined running flag (bit 63) and generation counter (bits 0-62).
998+ ///
999+ /// The generation increments with each guest function call to prevent
1000+ /// stale cancellations from affecting new calls (ABA problem).
1001+ ///
1002+ /// Layout: `[running:1 bit][generation:63 bits]`
1003+ running : AtomicU64 ,
1004+
1005+ /// Combined cancel_requested flag (bit 63) and generation counter (bits 0-62).
1006+ ///
1007+ /// When kill() is called, this stores the current generation along with
1008+ /// the cancellation flag. The VCPU only honors the cancellation if the
1009+ /// generation matches its current generation.
1010+ ///
1011+ /// Layout: `[cancel_requested:1 bit][generation:63 bits]`
1012+ cancel_requested : AtomicU64 ,
1013+
9701014 // This is used to signal the GDB thread to stop the vCPU
9711015 #[ cfg( gdb) ]
9721016 debug_interrupt : AtomicBool ,
1017+ /// Flag indicating whether a guest function call is currently in progress.
1018+ ///
1019+ /// **true**: A guest function call is active (between call start and completion)
1020+ /// **false**: No guest function call is active
1021+ ///
1022+ /// # Purpose
1023+ ///
1024+ /// This flag prevents kill() from having any effect when called outside of a
1025+ /// guest function call. This solves the "kill-in-advance" problem where kill()
1026+ /// could be called before a guest function starts and would incorrectly cancel it.
1027+ call_active : AtomicBool ,
9731028 partition_handle : WHV_PARTITION_HANDLE ,
9741029 dropped : AtomicBool ,
9751030}
9761031
9771032impl InterruptHandle for WindowsInterruptHandle {
9781033 fn kill ( & self ) -> bool {
979- self . cancel_requested . store ( true , Ordering :: Relaxed ) ;
980- self . running . load ( Ordering :: Relaxed )
981- && unsafe { WHvCancelRunVirtualProcessor ( self . partition_handle , 0 , 0 ) . is_ok ( ) }
1034+ // Check if a call is actually active first
1035+ if !self . call_active . load ( Ordering :: Acquire ) {
1036+ return false ;
1037+ }
1038+
1039+ // Get the current running state and generation
1040+ let ( running, generation) = self . get_running_and_generation ( ) ;
1041+
1042+ // Set cancel_requested with the current generation
1043+ self . set_cancel_requested ( generation) ;
1044+
1045+ // Only call WHvCancelRunVirtualProcessor if VCPU is actually running in guest mode
1046+ running && unsafe { WHvCancelRunVirtualProcessor ( self . partition_handle , 0 , 0 ) . is_ok ( ) }
9821047 }
9831048 #[ cfg( gdb) ]
9841049 fn kill_from_debugger ( & self ) -> bool {
9851050 self . debug_interrupt . store ( true , Ordering :: Relaxed ) ;
986- self . running . load ( Ordering :: Relaxed )
987- && unsafe { WHvCancelRunVirtualProcessor ( self . partition_handle , 0 , 0 ) . is_ok ( ) }
1051+ let ( running, _) = self . get_running_and_generation ( ) ;
1052+ running && unsafe { WHvCancelRunVirtualProcessor ( self . partition_handle , 0 , 0 ) . is_ok ( ) }
1053+ }
1054+
1055+ fn get_call_active ( & self ) -> & AtomicBool {
1056+ & self . call_active
1057+ }
1058+
1059+ fn get_dropped ( & self ) -> & AtomicBool {
1060+ & self . dropped
1061+ }
1062+
1063+ fn get_running ( & self ) -> & AtomicU64 {
1064+ & self . running
9881065 }
9891066
990- fn dropped ( & self ) -> bool {
991- self . dropped . load ( Ordering :: Relaxed )
1067+ fn get_cancel_requested ( & self ) -> & AtomicU64 {
1068+ & self . cancel_requested
9921069 }
9931070}
0 commit comments