From: Ralf Jung Date: Sat, 27 Jun 2020 12:35:58 +0000 (+0200) Subject: implement Windows SRWLock shims X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=8e9296994837c82c39b2c4cee7623d9181e4bc80;p=rust.git implement Windows SRWLock shims --- diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 704598ef2c6..0cd9ef05650 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -230,7 +230,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let active_thread = this.get_active_thread(); - assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported"); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); // Windows has a special magic linker section that is run on certain events. // Instead of searching for that section and supporting arbitrary hooks in there // (that would be basically https://github.com/rust-lang/miri/issues/450), diff --git a/src/shims/windows/dlsym.rs b/src/shims/windows/dlsym.rs index 34ed6ca150e..737fd4314f6 100644 --- a/src/shims/windows/dlsym.rs +++ b/src/shims/windows/dlsym.rs @@ -2,11 +2,16 @@ use crate::*; use helpers::check_arg_count; +use shims::windows::sync::EvalContextExt as _; #[derive(Debug, Copy, Clone)] pub enum Dlsym { AcquireSRWLockExclusive, + ReleaseSRWLockExclusive, + TryAcquireSRWLockExclusive, AcquireSRWLockShared, + ReleaseSRWLockShared, + TryAcquireSRWLockShared, } impl Dlsym { @@ -15,7 +20,11 @@ impl Dlsym { pub fn from_str(name: &str) -> InterpResult<'static, Option> { Ok(match name { "AcquireSRWLockExclusive" => Some(Dlsym::AcquireSRWLockExclusive), + "ReleaseSRWLockExclusive" => Some(Dlsym::ReleaseSRWLockExclusive), + "TryAcquireSRWLockExclusive" => Some(Dlsym::TryAcquireSRWLockExclusive), "AcquireSRWLockShared" => Some(Dlsym::AcquireSRWLockShared), + "ReleaseSRWLockShared" => Some(Dlsym::ReleaseSRWLockShared), + "TryAcquireSRWLockShared" => Some(Dlsym::TryAcquireSRWLockShared), "SetThreadStackGuarantee" => None, "GetSystemTimePreciseAsFileTime" => None, _ => throw_unsup_format!("unsupported Windows dlsym: {}", name), @@ -38,13 +47,29 @@ fn call_dlsym( match dlsym { Dlsym::AcquireSRWLockExclusive => { let &[ptr] = check_arg_count(args)?; - let lock = this.deref_operand(ptr)?; // points to ptr-sized data - throw_unsup_format!("AcquireSRWLockExclusive is not actually implemented"); + this.AcquireSRWLockExclusive(ptr)?; + } + Dlsym::ReleaseSRWLockExclusive => { + let &[ptr] = check_arg_count(args)?; + this.ReleaseSRWLockExclusive(ptr)?; + } + Dlsym::TryAcquireSRWLockExclusive => { + let &[ptr] = check_arg_count(args)?; + let ret = this.TryAcquireSRWLockExclusive(ptr)?; + this.write_scalar(Scalar::from_u8(ret), dest)?; } Dlsym::AcquireSRWLockShared => { let &[ptr] = check_arg_count(args)?; - let lock = this.deref_operand(ptr)?; // points to ptr-sized data - throw_unsup_format!("AcquireSRWLockExclusive is not actually implemented"); + this.AcquireSRWLockShared(ptr)?; + } + Dlsym::ReleaseSRWLockShared => { + let &[ptr] = check_arg_count(args)?; + this.ReleaseSRWLockShared(ptr)?; + } + Dlsym::TryAcquireSRWLockShared => { + let &[ptr] = check_arg_count(args)?; + let ret = this.TryAcquireSRWLockShared(ptr)?; + this.write_scalar(Scalar::from_u8(ret), dest)?; } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 2a30a234899..ddb70b752e7 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -21,6 +21,7 @@ fn emulate_foreign_item_by_name( // HANDLE = isize // DWORD = ULONG = u32 // BOOL = i32 + // BOOLEAN = u8 match link_name { // Environment related shims "GetEnvironmentVariableW" => { @@ -301,7 +302,7 @@ fn emulate_foreign_item_by_name( #[allow(non_snake_case)] let &[_lpCriticalSection] = check_arg_count(args)?; assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows not supported"); - // There is only one thread, so this always succeeds and returns TRUE + // There is only one thread, so this always succeeds and returns TRUE. this.write_scalar(Scalar::from_i32(1), dest)?; } diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index e69de29bb2d..ef40eb08911 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -0,0 +1,161 @@ +use rustc_target::abi::Size; + +use crate::*; + +// Locks are pointer-sized pieces of data, initialized to 0. +// We use them to count readers, with usize::MAX representing the write-locked state. + +fn deref_lock<'mir, 'tcx: 'mir>( + ecx: &mut MiriEvalContext<'mir, 'tcx>, + lock_op: OpTy<'tcx, Tag>, +) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + // `lock` is a pointer to `void*`; cast it to a pointer to `usize`. + let lock = ecx.deref_operand(lock_op)?; + let usize = ecx.machine.layouts.usize; + assert_eq!(lock.layout.size, usize.size); + Ok(lock.offset(Size::ZERO, MemPlaceMeta::None, usize, ecx)?) +} + +impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + #[allow(non_snake_case)] + fn AcquireSRWLockExclusive( + &mut self, + lock_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); + + let lock = deref_lock(this, lock_op)?; + let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?; + if lock_val == 0 { + // Currently not locked. Lock it. + let new_val = Scalar::from_machine_usize(this.machine_usize_max(), this); + this.write_scalar(new_val, lock.into())?; + } else { + // Lock is already held. This is a deadlock. + throw_machine_stop!(TerminationInfo::Deadlock); + } + + Ok(()) + } + + #[allow(non_snake_case)] + fn TryAcquireSRWLockExclusive( + &mut self, + lock_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, u8> { + let this = self.eval_context_mut(); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); + + let lock = deref_lock(this, lock_op)?; + let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?; + if lock_val == 0 { + // Currently not locked. Lock it. + let new_val = this.machine_usize_max(); + this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?; + Ok(1) + } else { + // Lock is already held. + Ok(0) + } + } + + #[allow(non_snake_case)] + fn ReleaseSRWLockExclusive( + &mut self, + lock_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); + + let lock = deref_lock(this, lock_op)?; + let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?; + if lock_val == this.machine_usize_max() { + // Currently locked. Unlock it. + let new_val = 0; + this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?; + } else { + // Lock is not locked. + throw_ub_format!("calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked"); + } + + Ok(()) + } + + #[allow(non_snake_case)] + fn AcquireSRWLockShared( + &mut self, + lock_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); + + let lock = deref_lock(this, lock_op)?; + let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?; + if lock_val == this.machine_usize_max() { + // Currently write locked. This is a deadlock. + throw_machine_stop!(TerminationInfo::Deadlock); + } else { + // Bump up read counter (cannot overflow as we just checkd against usize::MAX); + let new_val = lock_val+1; + // Make sure this does not reach the "write locked" flag. + if new_val == this.machine_usize_max() { + throw_unsup_format!("SRWLock read-acquired too many times"); + } + this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?; + } + + Ok(()) + } + + #[allow(non_snake_case)] + fn TryAcquireSRWLockShared( + &mut self, + lock_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, u8> { + let this = self.eval_context_mut(); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); + + let lock = deref_lock(this, lock_op)?; + let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?; + if lock_val == this.machine_usize_max() { + // Currently write locked. + Ok(0) + } else { + // Bump up read counter (cannot overflow as we just checkd against usize::MAX); + let new_val = lock_val+1; + // Make sure this does not reach the "write locked" flag. + if new_val == this.machine_usize_max() { + throw_unsup_format!("SRWLock read-acquired too many times"); + } + this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?; + Ok(1) + } + } + + #[allow(non_snake_case)] + fn ReleaseSRWLockShared( + &mut self, + lock_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported"); + + let lock = deref_lock(this, lock_op)?; + let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?; + if lock_val == this.machine_usize_max() { + // Currently write locked. This is a UB. + throw_ub_format!("calling ReleaseSRWLockShared on write-locked SRWLock"); + } else if lock_val == 0 { + // Currently not locked at all. + throw_ub_format!("calling ReleaseSRWLockShared on unlocked SRWLock"); + } else { + // Decrement read counter (cannot overflow as we just checkd against 0); + let new_val = lock_val-1; + this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?; + } + + Ok(()) + } +}