Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 9f69c41

Browse files
committed
rewrite handle impl again
1 parent 08ffbb8 commit 9f69c41

File tree

6 files changed

+90
-88
lines changed

6 files changed

+90
-88
lines changed

src/shims/windows/dlsym.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_target::spec::abi::Abi;
55
use log::trace;
66

77
use crate::helpers::check_arg_count;
8-
use crate::shims::windows::handle::{EvalContextExt as _, Handle};
8+
use crate::shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
99
use crate::*;
1010

1111
#[derive(Debug, Copy, Clone)]
@@ -112,14 +112,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
112112
Dlsym::SetThreadDescription => {
113113
let [handle, name] = check_arg_count(args)?;
114114

115+
let handle = this.read_scalar(handle)?.check_init()?;
116+
115117
let name = this.read_wide_str(this.read_pointer(name)?)?;
116118

117-
let thread =
118-
match Handle::from_scalar(this.read_scalar(handle)?.check_init()?, this)? {
119-
Some(Handle::Thread(thread)) => thread,
120-
Some(Handle::CurrentThread) => this.get_active_thread(),
121-
_ => this.invalid_handle("SetThreadDescription")?,
122-
};
119+
let thread = match Handle::from_scalar(handle, this)? {
120+
Some(Handle::Thread(thread)) => thread,
121+
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.get_active_thread(),
122+
_ => this.invalid_handle("SetThreadDescription")?,
123+
};
123124

124125
this.set_thread_name_wide(thread, name);
125126

src/shims/windows/foreign_items.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use std::iter;
2-
use std::time::{Duration, Instant};
32

43
use rustc_span::Symbol;
54
use rustc_target::abi::Size;
65
use rustc_target::spec::abi::Abi;
76

8-
use crate::thread::Time;
97
use crate::*;
108
use shims::foreign_items::EmulateByNameResult;
11-
use shims::windows::handle::{EvalContextExt as _, Handle};
9+
use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
1210
use shims::windows::sync::EvalContextExt as _;
1311
use shims::windows::thread::EvalContextExt as _;
1412

@@ -373,7 +371,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
373371
"GetCurrentThread" => {
374372
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
375373

376-
this.write_scalar(Handle::CurrentThread.to_scalar(this), dest)?;
374+
this.write_scalar(
375+
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
376+
dest,
377+
)?;
377378
}
378379

379380
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.

src/shims/windows/handle.rs

Lines changed: 69 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,79 @@ use std::mem::variant_count;
33

44
use crate::*;
55

6-
/// A Windows `HANDLE` that represents a resource instead of being null or a pseudohandle.
7-
///
8-
/// This is a seperate type from [`Handle`] to simplify the packing and unpacking code.
9-
#[derive(Clone, Copy)]
10-
enum RealHandle {
11-
Thread(ThreadId),
6+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7+
pub enum PseudoHandle {
8+
CurrentThread,
129
}
1310

14-
impl RealHandle {
15-
const USABLE_BITS: u32 = 31;
11+
impl PseudoHandle {
12+
const CURRENT_THREAD_VALUE: u32 = 0;
1613

17-
const THREAD_DISCRIMINANT: u32 = 1;
14+
fn value(self) -> u32 {
15+
match self {
16+
Self::CurrentThread => Self::CURRENT_THREAD_VALUE,
17+
}
18+
}
19+
20+
fn from_value(value: u32) -> Option<Self> {
21+
match value {
22+
Self::CURRENT_THREAD_VALUE => Some(Self::CurrentThread),
23+
_ => None,
24+
}
25+
}
26+
}
27+
28+
/// Miri representation of a Windows `HANDLE`
29+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30+
pub enum Handle {
31+
Null,
32+
Pseudo(PseudoHandle),
33+
Thread(ThreadId),
34+
}
35+
36+
impl Handle {
37+
const NULL_DISCRIMINANT: u32 = 0;
38+
const PSEUDO_DISCRIMINANT: u32 = 1;
39+
const THREAD_DISCRIMINANT: u32 = 2;
1840

1941
fn discriminant(self) -> u32 {
2042
match self {
21-
// can't use zero here because all zero handle is invalid
43+
Self::Null => Self::NULL_DISCRIMINANT,
44+
Self::Pseudo(_) => Self::PSEUDO_DISCRIMINANT,
2245
Self::Thread(_) => Self::THREAD_DISCRIMINANT,
2346
}
2447
}
2548

2649
fn data(self) -> u32 {
2750
match self {
51+
Self::Null => 0,
52+
Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
2853
Self::Thread(thread) => thread.to_u32(),
2954
}
3055
}
3156

3257
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>().ilog2() + 1
58+
// ceil(log2(x)) is how many bits it takes to store x numbers
59+
let variant_count = variant_count::<Self>();
60+
61+
// however, std's ilog2 is floor(log2(x))
62+
let floor_log2 = variant_count.ilog2();
63+
64+
// we need to add one for non powers of two to compensate for the difference
65+
let ceil_log2 = if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 };
66+
67+
ceil_log2
3668
}
3769

38-
/// This function packs the discriminant and data values into a 31-bit space.
70+
/// Converts a handle into its machine representation.
71+
///
72+
/// The upper [`Self::packed_disc_size()`] bits are used to store a discriminant corresponding to the handle variant.
73+
/// The remaining bits are used for the variant's field.
74+
///
3975
/// 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 {
76+
fn to_packed(self) -> u32 {
4277
let disc_size = Self::packed_disc_size();
43-
let data_size = Self::USABLE_BITS - disc_size;
78+
let data_size = u32::BITS - disc_size;
4479

4580
let discriminant = self.discriminant();
4681
let data = self.data();
@@ -53,90 +88,54 @@ impl RealHandle {
5388

5489
// packs the data into the lower `data_size` bits
5590
// and packs the discriminant right above the data
56-
(discriminant << data_size | data) as i32
91+
discriminant << data_size | data
5792
}
5893

5994
fn new(discriminant: u32, data: u32) -> Option<Self> {
6095
match discriminant {
96+
Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
97+
Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
6198
Self::THREAD_DISCRIMINANT => Some(Self::Thread(data.into())),
6299
_ => None,
63100
}
64101
}
65102

66103
/// see docs for `to_packed`
67-
fn from_packed(handle: i32) -> Option<Self> {
68-
let handle_bits = handle as u32;
69-
104+
fn from_packed(handle: u32) -> Option<Self> {
70105
let disc_size = Self::packed_disc_size();
71-
let data_size = Self::USABLE_BITS - disc_size;
106+
let data_size = u32::BITS - disc_size;
72107

73108
// the lower `data_size` bits of this mask are 1
74109
let data_mask = 2u32.pow(data_size) - 1;
75110

76111
// the discriminant is stored right above the lower `data_size` bits
77-
let discriminant = handle_bits >> data_size;
112+
let discriminant = handle >> data_size;
78113

79114
// the data is stored in the lower `data_size` bits
80-
let data = handle_bits & data_mask;
115+
let data = handle & data_mask;
81116

82117
Self::new(discriminant, data)
83118
}
84-
}
85-
86-
/// Miri representation of a Windows `HANDLE`
87-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
88-
pub enum Handle {
89-
Null, // = 0
90-
91-
// pseudo-handles
92-
// The lowest real windows pseudo-handle is -6, so miri pseduo-handles start at -7 to break code hardcoding these values
93-
CurrentThread, // = -7
94-
95-
// real handles
96-
Thread(ThreadId),
97-
}
98-
99-
impl Handle {
100-
const CURRENT_THREAD_VALUE: i32 = -7;
101-
102-
fn to_packed(self) -> i32 {
103-
match self {
104-
Self::Null => 0,
105-
Self::CurrentThread => Self::CURRENT_THREAD_VALUE,
106-
Self::Thread(thread) => RealHandle::Thread(thread).to_packed(),
107-
}
108-
}
109119

110120
pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar<Provenance> {
111121
// 64-bit handles are sign extended 32-bit handles
112122
// see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
113-
let handle = self.to_packed().into();
114-
115-
Scalar::from_machine_isize(handle, cx)
116-
}
117-
118-
fn from_packed(handle: i64) -> Option<Self> {
119-
let current_thread_val = Self::CURRENT_THREAD_VALUE as i64;
120-
121-
if handle == 0 {
122-
Some(Self::Null)
123-
} else if handle == current_thread_val {
124-
Some(Self::CurrentThread)
125-
} else if let Ok(handle) = handle.try_into() {
126-
match RealHandle::from_packed(handle)? {
127-
RealHandle::Thread(id) => Some(Self::Thread(id)),
128-
}
129-
} else {
130-
// if a handle doesn't fit in an i32, it isn't valid.
131-
None
132-
}
123+
let signed_handle = self.to_packed() as i32;
124+
Scalar::from_machine_isize(signed_handle.into(), cx)
133125
}
134126

135127
pub fn from_scalar<'tcx>(
136128
handle: Scalar<Provenance>,
137129
cx: &impl HasDataLayout,
138130
) -> InterpResult<'tcx, Option<Self>> {
139-
let handle = handle.to_machine_isize(cx)?;
131+
let sign_extended_handle = handle.to_machine_isize(cx)?;
132+
133+
let handle = if let Ok(signed_handle) = i32::try_from(sign_extended_handle) {
134+
signed_handle as u32
135+
} else {
136+
// if a handle doesn't fit in an i32, it isn't valid.
137+
return Ok(None);
138+
};
140139

141140
Ok(Self::from_packed(handle))
142141
}

src/shims/windows/thread.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_middle::ty::layout::LayoutOf;
22
use rustc_target::spec::abi::Abi;
33

44
use crate::*;
5-
use shims::windows::handle::{EvalContextExt as _, Handle};
5+
use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
66

77
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
88

@@ -58,7 +58,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5858
Some(Handle::Thread(thread)) => thread,
5959
// Unlike on posix, joining the current thread is not UB on windows.
6060
// It will just deadlock.
61-
Some(Handle::CurrentThread) => this.get_active_thread(),
61+
Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.get_active_thread(),
6262
_ => this.invalid_handle("WaitForSingleObject")?,
6363
};
6464

tests/fail/concurrency/windows_join_main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
use std::thread;
88

99
extern "system" {
10-
fn WaitForSingleObject(handle: usize, timeout: u32) -> u32;
10+
fn WaitForSingleObject(handle: isize, timeout: u32) -> u32;
1111
}
1212

1313
const INFINITE: u32 = u32::MAX;
1414

1515
// This is how miri represents the handle for thread 0.
1616
// This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle`
1717
// but miri does not implement `DuplicateHandle` yet.
18-
const MAIN_THREAD: usize = 1 << 30;
18+
const MAIN_THREAD: isize = (2i32 << 30) as isize;
1919

2020
fn main() {
2121
thread::spawn(|| {

tests/fail/concurrency/windows_join_main.stderr

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
error: deadlock: the evaluated program deadlocked
22
--> $DIR/windows_join_main.rs:LL:CC
33
|
4-
LL | WaitForSingleObject(MAIN_THREAD, INFINITE);
5-
| ^ the evaluated program deadlocked
4+
LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), 0);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked
66
|
7-
= note: inside closure at $DIR/windows_join_main.rs:LL:CC
7+
= note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC
8+
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
89

910
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
1011

0 commit comments

Comments
 (0)