88//! validating, verifying and generating artefacts for the driver package.
99
1010use std:: {
11+ ffi:: { CStr , CString } ,
12+ marker:: PhantomData ,
1113 ops:: RangeFrom ,
1214 path:: { Path , PathBuf } ,
1315 result:: Result ,
@@ -16,6 +18,13 @@ use std::{
1618use mockall_double:: double;
1719use tracing:: { debug, info, warn} ;
1820use wdk_build:: { CpuArchitecture , DriverConfig } ;
21+ use windows:: {
22+ Win32 :: {
23+ Foundation :: { CloseHandle , GetLastError , HANDLE , WAIT_ABANDONED , WAIT_OBJECT_0 } ,
24+ System :: Threading :: { CreateMutexA , INFINITE , ReleaseMutex , WaitForSingleObject } ,
25+ } ,
26+ core:: { Error as WinError , PCSTR } ,
27+ } ;
1928
2029#[ double]
2130use crate :: providers:: { exec:: CommandExec , fs:: Fs , wdk_build:: WdkBuild } ;
@@ -368,8 +377,26 @@ impl<'a> PackageTask<'a> {
368377 if self . is_self_signed_certificate_in_store ( ) ? {
369378 self . create_cert_file_from_store ( ) ?;
370379 } else {
371- self . create_self_signed_cert_in_store ( ) ?;
380+ // This mutex prevents multiple instances of this app from racing to
381+ // create a cert in the store. It is not a correctness problem. We
382+ // just don't want to litter the store with certs especially during
383+ // tests when there are lots of parallel runs
384+ let mutex_name = CString :: new ( "WDRCertStoreMutex_bd345cf9330" ) // Unique enough
385+ . expect ( "string is a valid C string" ) ;
386+ let mutex = NamedMutex :: acquire ( & mutex_name)
387+ . map_err ( |e| PackageTaskError :: CertMutexError ( e. code ( ) . 0 ) ) ?;
388+ debug ! ( "Acquired cert store mutex" ) ;
389+
390+ // Check again for an existing cert. Another instance might have
391+ // created it while we waited for the mutex
392+ if self . is_self_signed_certificate_in_store ( ) ? {
393+ drop ( mutex) ;
394+ self . create_cert_file_from_store ( ) ?;
395+ } else {
396+ self . create_self_signed_cert_in_store ( ) ?;
397+ }
372398 }
399+
373400 Ok ( ( ) )
374401 }
375402
@@ -527,6 +554,62 @@ impl<'a> PackageTask<'a> {
527554 Ok ( ( ) )
528555 }
529556}
557+
558+ /// An RAII wrapper over a Win API named mutex
559+ struct NamedMutex {
560+ handle : HANDLE ,
561+ // `ReleaseMutex` requires that it is called
562+ // only by threads that own the mutex handle.
563+ // Being `!Send` ensures that's always the case.
564+ _not_send : PhantomData < * const ( ) > ,
565+ }
566+
567+ impl NamedMutex {
568+ /// Acquires named mutex
569+ pub fn acquire ( name : & CStr ) -> Result < Self , WinError > {
570+ fn get_last_error ( ) -> WinError {
571+ // SAFETY: We have to just assume this function is safe to call
572+ // because the windows crate has no documentation for it and
573+ // the MSDN documentation does not specify any preconditions
574+ // for calling it
575+ unsafe { GetLastError ( ) . into ( ) }
576+ }
577+
578+ // SAFETY: The name ptr is valid because it comes from a CStr
579+ let handle = unsafe { CreateMutexA ( None , false , PCSTR ( name. as_ptr ( ) . cast ( ) ) ) ? } ;
580+ if handle. is_invalid ( ) {
581+ return Err ( get_last_error ( ) ) ;
582+ }
583+
584+ // SAFETY: The handle is valid since it was created right above
585+ match unsafe { WaitForSingleObject ( handle, INFINITE ) } {
586+ res if res == WAIT_OBJECT_0 || res == WAIT_ABANDONED => Ok ( Self {
587+ handle,
588+ _not_send : PhantomData ,
589+ } ) ,
590+ _ => {
591+ // SAFETY: The handle is valid since it was created right above
592+ unsafe { CloseHandle ( handle) ? } ;
593+ Err ( get_last_error ( ) )
594+ }
595+ }
596+ }
597+ }
598+
599+ impl Drop for NamedMutex {
600+ fn drop ( & mut self ) {
601+ // SAFETY: the handle is guaranteed to be valid
602+ // because this type itself created it and it
603+ // was never exposed outside. Also the requirement
604+ // that the calling thread must own the handle
605+ // is upheld because this type is `!Send`
606+ let _ = unsafe { ReleaseMutex ( self . handle ) } ;
607+
608+ // SAFETY: the handle is valid as explained above.
609+ let _ = unsafe { CloseHandle ( self . handle ) } ;
610+ }
611+ }
612+
530613#[ cfg( test) ]
531614mod tests {
532615 use std:: {
@@ -715,4 +798,93 @@ mod tests {
715798 ) ;
716799 }
717800 }
801+
802+ mod named_mutex {
803+ use std:: {
804+ ffi:: CString ,
805+ sync:: {
806+ Barrier ,
807+ atomic:: { AtomicUsize , Ordering } ,
808+ } ,
809+ thread,
810+ time:: Duration ,
811+ } ;
812+
813+ use super :: super :: NamedMutex ;
814+
815+ /// Tests that two threads successfully acquire `NamedMutex`
816+ /// and it prevents them from running concurrently.
817+ #[ test]
818+ fn acquire_works_correctly ( ) {
819+ // The way this test work is:
820+ // 1. We create two threads that start at the same time thanks
821+ // to a barrier
822+ // 2. Both increment a counter `active` while they run holding
823+ // the mutex
824+ // 3. Both also increment another counter `completed` when they finish
825+ // 4. We verify that `active` never exceeds 1 i.e. there's no concurrent
826+ // execution and `completed` is 2 at the end i.e. both threads run to completion
827+
828+ let barrier = Barrier :: new ( 2 ) ;
829+ let active = AtomicUsize :: new ( 0 ) ;
830+ let completed = AtomicUsize :: new ( 0 ) ;
831+
832+ thread:: scope ( |s| {
833+ for _ in 0 ..2 {
834+ s. spawn ( || {
835+ let name =
836+ CString :: new ( "happy_path_d44f8b8a817" ) . expect ( "it is a valid C string" ) ;
837+
838+ barrier. wait ( ) ;
839+ let guard = NamedMutex :: acquire ( name. as_c_str ( ) )
840+ . expect ( "thread should acquire mutex" ) ;
841+
842+ let active_prev = active. fetch_add ( 1 , Ordering :: SeqCst ) ;
843+ assert_eq ! ( active_prev, 0 , "named mutex allowed concurrent access" ) ;
844+
845+ thread:: sleep ( Duration :: from_millis ( 100 ) ) ;
846+
847+ let active_prev = active. fetch_sub ( 1 , Ordering :: SeqCst ) ;
848+ assert_eq ! ( active_prev, 1 , "active counter should drop back to zero" ) ;
849+
850+ drop ( guard) ;
851+
852+ completed. fetch_add ( 1 , Ordering :: SeqCst ) ;
853+ } ) ;
854+ }
855+ } ) ;
856+
857+ assert_eq ! ( completed. load( Ordering :: SeqCst ) , 2 ) ;
858+ assert_eq ! ( active. load( Ordering :: SeqCst ) , 0 ) ;
859+ }
860+
861+ /// Tests that `NamedMutex` can be acquired even after the previous
862+ /// owner abandoned it (e.g. crashed) without releasing
863+ ///
864+ /// What we are really testing here is `WaitForSingleObject`
865+ /// inside `NamedMutex::acquire` returning `WAIT_ABANDONED`
866+ #[ test]
867+ fn acquire_works_when_abandoned ( ) {
868+ fn acquire_mutex ( ) -> NamedMutex {
869+ let name =
870+ CString :: new ( "abandoned_owner_d44f8b8a817" ) . expect ( "it is a valid C string" ) ;
871+ NamedMutex :: acquire ( name. as_c_str ( ) ) . expect ( "thread should acquire mutex" )
872+ }
873+
874+ // Acquire the mutex on a thread and abandon it
875+ thread:: scope ( |s| {
876+ s. spawn ( || {
877+ let guard = acquire_mutex ( ) ;
878+ // Simulate an abnormal exit while still holding the mutex to trigger the
879+ // WAIT_ABANDONED path for the next owner.
880+ std:: mem:: forget ( guard) ;
881+ } ) ;
882+ } ) ;
883+
884+ // Try to acquire the same mutex from the main thread
885+ // which should succeed despite the abandonment above
886+ let guard = acquire_mutex ( ) ;
887+ drop ( guard) ;
888+ }
889+ }
718890}
0 commit comments