Skip to content

Commit e9c2199

Browse files
committed
handle rewrite
1 parent 0b296a5 commit e9c2199

File tree

1 file changed

+46
-34
lines changed

1 file changed

+46
-34
lines changed

src/shims/windows/handle.rs

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,42 @@ enum RealHandle {
1212
}
1313

1414
impl RealHandle {
15-
fn discriminant(self) -> u64 {
15+
const USABLE_BITS: u32 = 31;
16+
17+
fn discriminant(self) -> u32 {
1618
match self {
1719
// can't use zero here because all zero handle is invalid
1820
Self::Thread(_) => 1,
1921
}
2022
}
2123

22-
fn data(self) -> u64 {
24+
fn data(self) -> u32 {
2325
match self {
24-
Self::Thread(thread) => thread.to_u32() as u64,
26+
Self::Thread(thread) => thread.to_u32(),
2527
}
2628
}
2729

28-
fn packed_disc_size() -> u64 {
29-
(variant_count::<Self>().log2() + 1) as u64
30+
fn packed_disc_size() -> u32 {
31+
(variant_count::<Self>().log2() + 1)
32+
.try_into()
33+
.expect("this would require more than 2^4294967294 variants to overflow")
3034
}
3135

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-
36+
/// This function packs the discriminant and data values into a 31-bit space.
37+
/// None of this layout is guaranteed to applications by Windows or Miri.
38+
/// The sign bit is not used to avoid overlapping any pseudo-handles.
39+
fn to_packed(self) -> i32 {
3640
let disc_size = Self::packed_disc_size();
37-
let data_size = usable_bits - disc_size;
41+
let data_size = Self::USABLE_BITS - disc_size;
3842

3943
let discriminant = self.discriminant();
4044
let data = self.data();
4145

42-
assert!(discriminant < 2u64.pow(disc_size as u32));
43-
assert!(data < 2u64.pow(data_size as u32));
46+
// these assertions ensure the components avoid overlapping eachother and the sign bit
47+
assert!(discriminant < 2u32.pow(disc_size));
48+
assert!(data < 2u32.pow(data_size));
4449

45-
(discriminant << data_size | data) << 2
50+
(discriminant << data_size | data) as i32
4651
}
4752

4853
fn new(discriminant: u32, data: u32) -> Option<Self> {
@@ -52,67 +57,74 @@ impl RealHandle {
5257
}
5358
}
5459

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

5864
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;
65+
let data_size = Self::USABLE_BITS - disc_size;
66+
67+
// the lower `data_size` bits of this mask are 1
68+
let data_mask = 2u32.pow(data_size) - 1;
6169

62-
let discriminant = handle >> data_size >> 2;
63-
let data = handle >> 2 & data_mask;
70+
let discriminant = handle_bits >> data_size;
71+
let data = handle_bits & data_mask;
6472

65-
Self::new(discriminant.try_into().unwrap(), data.try_into().unwrap())
73+
Self::new(discriminant, data)
6674
}
6775
}
6876

6977
/// Miri representation of a Windows `HANDLE`
7078
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7179
pub enum Handle {
7280
Null, // = 0
81+
7382
// pseudo-handles
74-
CurrentThread, // = -2
83+
// The lowest pseudo-handle is -6, so miri pseduo-handles start at -7 to break code hardcoding these values
84+
CurrentThread, // = -7
85+
7586
// real handles
7687
Thread(ThreadId),
7788
}
7889

7990
impl Handle {
80-
fn to_packed(self, bits: u64) -> i64 {
91+
fn to_packed(self) -> i32 {
8192
match self {
8293
Self::Null => 0,
83-
Self::CurrentThread => -2,
84-
Self::Thread(thread) => RealHandle::Thread(thread).to_packed(bits) as i64,
94+
Self::CurrentThread => -7,
95+
Self::Thread(thread) => RealHandle::Thread(thread).to_packed(),
8596
}
8697
}
8798

8899
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);
100+
// 64-bit handles are sign extended 32-bit handles
101+
// see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
102+
let handle = self.to_packed().into();
92103

93104
Scalar::from_machine_isize(handle, cx)
94105
}
95106

96-
fn from_packed(handle: i64, bits: u64) -> Option<Self> {
107+
fn from_packed(handle: i64) -> Option<Self> {
97108
if handle == 0 {
98109
Some(Self::Null)
99-
} else if handle == -2 {
110+
} else if handle == -7 {
100111
Some(Self::CurrentThread)
101-
} else {
102-
match RealHandle::from_packed(handle as u64, bits)? {
112+
} else if let Ok(handle) = handle.try_into() {
113+
match RealHandle::from_packed(handle)? {
103114
RealHandle::Thread(id) => Some(Self::Thread(id)),
104115
}
116+
} else {
117+
// if a handle doesn't fit in an i32, it isn't valid.
118+
None
105119
}
106120
}
107121

108122
pub fn from_scalar<'tcx>(
109123
handle: Scalar<Tag>,
110124
cx: &impl HasDataLayout,
111125
) -> InterpResult<'tcx, Option<Self>> {
112-
let bits = cx.data_layout().pointer_size.bits();
113-
114126
let handle = handle.to_machine_isize(cx)?;
115127

116-
Ok(Self::from_packed(handle, bits))
128+
Ok(Self::from_packed(handle))
117129
}
118130
}

0 commit comments

Comments
 (0)