Skip to content

Commit 556ff34

Browse files
committed
handle rewrite
1 parent 72c11b6 commit 556ff34

File tree

2 files changed

+82
-41
lines changed

2 files changed

+82
-41
lines changed

src/shims/windows/foreign_items.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_target::spec::abi::Abi;
99
use crate::thread::Time;
1010
use crate::*;
1111
use shims::foreign_items::EmulateByNameResult;
12-
use shims::windows::handle::Handle;
12+
use shims::windows::handle::{EvalContextExt as _, Handle};
1313
use shims::windows::sync::EvalContextExt as _;
1414
use shims::windows::thread::EvalContextExt as _;
1515

@@ -393,10 +393,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
393393
let [handle] =
394394
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
395395

396-
match Handle::from_scalar(this.read_scalar(handle)?.check_init()?, this)? {
397-
Some(Handle::Thread(thread)) => this.detach_thread(thread)?,
398-
_ => throw_ub_format!("invalid handle"),
399-
};
396+
this.CloseHandle(handle)?;
400397

401398
this.write_scalar(Scalar::from_u32(1), dest)?;
402399
}

src/shims/windows/handle.rs

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,107 +12,151 @@ enum RealHandle {
1212
}
1313

1414
impl RealHandle {
15-
fn discriminant(self) -> u64 {
15+
const USABLE_BITS: u32 = 31;
16+
17+
const THREAD_DISCRIMINANT: u32 = 1;
18+
19+
fn discriminant(self) -> u32 {
1620
match self {
1721
// can't use zero here because all zero handle is invalid
18-
Self::Thread(_) => 1,
22+
Self::Thread(_) => Self::THREAD_DISCRIMINANT,
1923
}
2024
}
2125

22-
fn data(self) -> u64 {
26+
fn data(self) -> u32 {
2327
match self {
24-
Self::Thread(thread) => thread.to_u32() as u64,
28+
Self::Thread(thread) => thread.to_u32(),
2529
}
2630
}
2731

28-
fn packed_disc_size() -> u64 {
29-
(variant_count::<Self>().log2() + 1) as u64
32+
fn packed_disc_size() -> u32 {
33+
// log2(x) + 1 is how many bits it takes to store x
34+
// because the discriminants start at 1, the variant count is equal to the highest discriminant
35+
variant_count::<Self>().log2() + 1
3036
}
3137

32-
fn to_packed(self, bits: u64) -> u64 {
33-
// top bit and lower 2 bits need to be clear
34-
let usable_bits = (bits - 3).min(32);
35-
38+
/// This function packs the discriminant and data values into a 31-bit space.
39+
/// None of this layout is guaranteed to applications by Windows or Miri.
40+
/// The sign bit is not used to avoid overlapping any pseudo-handles.
41+
fn to_packed(self) -> i32 {
3642
let disc_size = Self::packed_disc_size();
37-
let data_size = usable_bits - disc_size;
43+
let data_size = Self::USABLE_BITS - disc_size;
3844

3945
let discriminant = self.discriminant();
4046
let data = self.data();
4147

42-
assert!(discriminant < 2u64.pow(disc_size as u32));
43-
assert!(data < 2u64.pow(data_size as u32));
48+
// make sure the discriminant fits into `disc_size` bits
49+
assert!(discriminant < 2u32.pow(disc_size));
4450

45-
(discriminant << data_size | data) << 2
51+
// make sure the data fits into `data_size` bits
52+
assert!(data < 2u32.pow(data_size));
53+
54+
// packs the data into the lower `data_size` bits
55+
// and packs the discriminant right above the data
56+
(discriminant << data_size | data) as i32
4657
}
4758

4859
fn new(discriminant: u32, data: u32) -> Option<Self> {
4960
match discriminant {
50-
1 => Some(Self::Thread(data.into())),
61+
Self::THREAD_DISCRIMINANT => Some(Self::Thread(data.into())),
5162
_ => None,
5263
}
5364
}
5465

55-
fn from_packed(handle: u64, bits: u64) -> Option<Self> {
56-
let usable_bits = (bits - 3).min(32);
66+
/// see docs for `to_packed`
67+
fn from_packed(handle: i32) -> Option<Self> {
68+
let handle_bits = handle as u32;
5769

5870
let disc_size = Self::packed_disc_size();
59-
let data_size = usable_bits - disc_size;
60-
let data_mask = 2u64.pow(data_size as u32) - 1;
71+
let data_size = Self::USABLE_BITS - disc_size;
72+
73+
// the lower `data_size` bits of this mask are 1
74+
let data_mask = 2u32.pow(data_size) - 1;
6175

62-
let discriminant = handle >> data_size >> 2;
63-
let data = handle >> 2 & data_mask;
76+
// the discriminant is stored right above the lower `data_size` bits
77+
let discriminant = handle_bits >> data_size;
6478

65-
Self::new(discriminant.try_into().unwrap(), data.try_into().unwrap())
79+
// the data is stored in the lower `data_size` bits
80+
let data = handle_bits & data_mask;
81+
82+
Self::new(discriminant, data)
6683
}
6784
}
6885

6986
/// Miri representation of a Windows `HANDLE`
7087
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7188
pub enum Handle {
7289
Null, // = 0
90+
7391
// pseudo-handles
74-
CurrentThread, // = -2
92+
// The lowest pseudo-handle is -6, so miri pseduo-handles start at -7 to break code hardcoding these values
93+
CurrentThread, // = -7
94+
7595
// real handles
7696
Thread(ThreadId),
7797
}
7898

7999
impl Handle {
80-
fn to_packed(self, bits: u64) -> i64 {
100+
const CURRENT_THREAD_VALUE: i32 = -7;
101+
102+
fn to_packed(self) -> i32 {
81103
match self {
82104
Self::Null => 0,
83-
Self::CurrentThread => -2,
84-
Self::Thread(thread) => RealHandle::Thread(thread).to_packed(bits) as i64,
105+
Self::CurrentThread => Self::CURRENT_THREAD_VALUE,
106+
Self::Thread(thread) => RealHandle::Thread(thread).to_packed(),
85107
}
86108
}
87109

88110
pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar<Tag> {
89-
let bits = cx.data_layout().pointer_size.bits();
90-
91-
let handle = self.to_packed(bits);
111+
// 64-bit handles are sign extended 32-bit handles
112+
// see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
113+
let handle = self.to_packed().into();
92114

93115
Scalar::from_machine_isize(handle, cx)
94116
}
95117

96-
fn from_packed(handle: i64, bits: u64) -> Option<Self> {
118+
fn from_packed(handle: i64) -> Option<Self> {
119+
let current_thread_val = Self::CURRENT_THREAD_VALUE as i64;
120+
97121
if handle == 0 {
98122
Some(Self::Null)
99-
} else if handle == -2 {
123+
} else if handle == current_thread_val {
100124
Some(Self::CurrentThread)
101-
} else {
102-
match RealHandle::from_packed(handle as u64, bits)? {
125+
} else if let Ok(handle) = handle.try_into() {
126+
match RealHandle::from_packed(handle)? {
103127
RealHandle::Thread(id) => Some(Self::Thread(id)),
104128
}
129+
} else {
130+
// if a handle doesn't fit in an i32, it isn't valid.
131+
None
105132
}
106133
}
107134

108135
pub fn from_scalar<'tcx>(
109136
handle: Scalar<Tag>,
110137
cx: &impl HasDataLayout,
111138
) -> InterpResult<'tcx, Option<Self>> {
112-
let bits = cx.data_layout().pointer_size.bits();
113-
114139
let handle = handle.to_machine_isize(cx)?;
115140

116-
Ok(Self::from_packed(handle, bits))
141+
Ok(Self::from_packed(handle))
142+
}
143+
}
144+
145+
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
146+
147+
#[allow(non_snake_case)]
148+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
149+
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
150+
let this = self.eval_context_mut();
151+
152+
match Handle::from_scalar(this.read_scalar(handle_op)?.check_init()?, this)? {
153+
Some(Handle::Thread(thread)) => this.detach_thread(thread)?,
154+
_ =>
155+
throw_machine_stop!(TerminationInfo::Abort(
156+
"invalid handle passed to `CloseHandle`".into()
157+
)),
158+
};
159+
160+
Ok(())
117161
}
118162
}

0 commit comments

Comments
 (0)