implement Windows SRWLock shims

This commit is contained in:
Ralf Jung 2020-06-27 14:35:58 +02:00
parent dca00ab85e
commit 8e92969948
4 changed files with 193 additions and 6 deletions

View file

@ -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),

View file

@ -2,11 +2,16 @@ use rustc_middle::mir;
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<Dlsym>> {
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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?;
}
}

View file

@ -21,6 +21,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// HANDLE = isize
// DWORD = ULONG = u32
// BOOL = i32
// BOOLEAN = u8
match link_name {
// Environment related shims
"GetEnvironmentVariableW" => {
@ -301,7 +302,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
#[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)?;
}

View file

@ -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(())
}
}