@@ -338,8 +338,23 @@ pub trait InterruptHandle: Send + Sync {
338338#[ cfg( any( kvm,  mshv) ) ]  
339339#[ derive( Debug ) ]  
340340pub ( super )  struct  LinuxInterruptHandle  { 
341-     /// Invariant: vcpu is running => `running` is true. (Neither converse nor inverse is true) 
342-      running :  AtomicBool , 
341+     /// Invariant: vcpu is running => most significant bit (63) of `running` is set. (Neither converse nor inverse is true) 
342+      /// 
343+      /// Additionally, bit 0-62 tracks how many times the VCPU has been run. Incremented each time `run()` is called. 
344+      /// 
345+      /// This prevents an ABA problem where: 
346+      /// 1. The VCPU is running (generation N), 
347+      /// 2. It gets cancelled, 
348+      /// 3. Then quickly restarted (generation N+1), 
349+      ///     before the original thread has observed that it was cancelled. 
350+      /// 
351+      /// Without this generation counter, the interrupt logic might assume the VCPU is still 
352+      /// in the *original* run (generation N), see that it's `running`, and re-send the signal. 
353+      /// But the new VCPU run (generation N+1) would treat this as a stale signal and ignore it, 
354+      /// potentially causing an infinite loop where no effective interrupt is delivered. 
355+      /// 
356+      /// Invariant: If the VCPU is running, `run_generation[bit 0-62]` matches the current run's generation. 
357+      running :  AtomicU64 , 
343358    /// Invariant: vcpu is running => `tid` is the thread on which it is running. 
344359     /// Note: multiple vms may have the same `tid`, but at most one vm will have `running` set to true. 
345360     tid :  AtomicU64 , 
@@ -359,15 +374,60 @@ pub(super) struct LinuxInterruptHandle {
359374     sig_rt_min_offset :  u8 , 
360375} 
361376
377+ impl  LinuxInterruptHandle  { 
378+     const  RUNNING_BIT :  u64  = 1  << 63 ; 
379+     const  MAX_GENERATION :  u64  = Self :: RUNNING_BIT  - 1 ; 
380+ 
381+     // set running to true and increment the generation. Generation will wrap around at `MAX_GENERATION`. 
382+     fn  set_running_and_increment_generation ( & self )  -> std:: result:: Result < u64 ,  u64 >  { 
383+         self . running 
384+             . fetch_update ( Ordering :: Relaxed ,  Ordering :: Relaxed ,  |raw| { 
385+                 let  generation = raw &  !Self :: RUNNING_BIT ; 
386+                 if  generation == Self :: MAX_GENERATION  { 
387+                     // restart generation from 0 
388+                     return  Some ( Self :: RUNNING_BIT ) ; 
389+                 } 
390+                 Some ( ( generation + 1 )  | Self :: RUNNING_BIT ) 
391+             } ) 
392+     } 
393+ 
394+     // clear the running bit and return the generation 
395+     fn  clear_running_bit ( & self )  -> u64  { 
396+         self . running 
397+             . fetch_and ( !Self :: RUNNING_BIT ,  Ordering :: Relaxed ) 
398+     } 
399+ 
400+     fn  get_running_and_generation ( & self )  -> ( bool ,  u64 )  { 
401+         let  raw = self . running . load ( Ordering :: Relaxed ) ; 
402+         let  running = raw &  Self :: RUNNING_BIT  != 0 ; 
403+         let  generation = raw &  !Self :: RUNNING_BIT ; 
404+         ( running,  generation) 
405+     } 
406+ } 
407+ 
362408#[ cfg( any( kvm,  mshv) ) ]  
363409impl  InterruptHandle  for  LinuxInterruptHandle  { 
364410    fn  kill ( & self )  -> bool  { 
365411        self . cancel_requested . store ( true ,  Ordering :: Relaxed ) ; 
366412
367413        let  signal_number = libc:: SIGRTMIN ( )  + self . sig_rt_min_offset  as  libc:: c_int ; 
368414        let  mut  sent_signal = false ; 
415+         let  mut  target_generation:  Option < u64 >  = None ; 
416+ 
417+         loop  { 
418+             let  ( running,  generation)  = self . get_running_and_generation ( ) ; 
419+ 
420+             if  !running { 
421+                 break ; 
422+             } 
423+ 
424+             match  target_generation { 
425+                 None  => target_generation = Some ( generation) , 
426+                 // prevent ABA problem 
427+                 Some ( expected)  if  expected != generation => break , 
428+                 _ => { } 
429+             } 
369430
370-         while  self . running . load ( Ordering :: Relaxed )  { 
371431            log:: info!( "Sending signal to kill vcpu thread..." ) ; 
372432            sent_signal = true ; 
373433            unsafe  { 
0 commit comments