miri/shims/windows/
handle.rs
1use std::mem::variant_count;
2
3use rustc_abi::HasDataLayout;
4
5use crate::concurrency::thread::ThreadNotFound;
6use crate::*;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub enum PseudoHandle {
10 CurrentThread,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
15pub enum Handle {
16 Null,
17 Pseudo(PseudoHandle),
18 Thread(ThreadId),
19}
20
21impl PseudoHandle {
22 const CURRENT_THREAD_VALUE: u32 = 0;
23
24 fn value(self) -> u32 {
25 match self {
26 Self::CurrentThread => Self::CURRENT_THREAD_VALUE,
27 }
28 }
29
30 fn from_value(value: u32) -> Option<Self> {
31 match value {
32 Self::CURRENT_THREAD_VALUE => Some(Self::CurrentThread),
33 _ => None,
34 }
35 }
36}
37
38pub enum HandleError {
40 ThreadNotFound(ThreadNotFound),
42 InvalidHandle,
44}
45
46impl Handle {
47 const NULL_DISCRIMINANT: u32 = 0;
48 const PSEUDO_DISCRIMINANT: u32 = 1;
49 const THREAD_DISCRIMINANT: u32 = 2;
50
51 fn discriminant(self) -> u32 {
52 match self {
53 Self::Null => Self::NULL_DISCRIMINANT,
54 Self::Pseudo(_) => Self::PSEUDO_DISCRIMINANT,
55 Self::Thread(_) => Self::THREAD_DISCRIMINANT,
56 }
57 }
58
59 fn data(self) -> u32 {
60 match self {
61 Self::Null => 0,
62 Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
63 Self::Thread(thread) => thread.to_u32(),
64 }
65 }
66
67 fn packed_disc_size() -> u32 {
68 let variant_count = variant_count::<Self>();
70
71 let floor_log2 = variant_count.ilog2();
73
74 #[expect(clippy::arithmetic_side_effects)] if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 }
77 }
78
79 fn to_packed(self) -> u32 {
86 let disc_size = Self::packed_disc_size();
87 let data_size = u32::BITS.strict_sub(disc_size);
88
89 let discriminant = self.discriminant();
90 let data = self.data();
91
92 assert!(discriminant < 2u32.pow(disc_size));
94
95 assert!(data < 2u32.pow(data_size));
97
98 (discriminant << data_size) | data
101 }
102
103 fn new(discriminant: u32, data: u32) -> Option<Self> {
104 match discriminant {
105 Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
106 Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
107 Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))),
108 _ => None,
109 }
110 }
111
112 fn from_packed(handle: u32) -> Option<Self> {
114 let disc_size = Self::packed_disc_size();
115 let data_size = u32::BITS.strict_sub(disc_size);
116
117 #[expect(clippy::arithmetic_side_effects)] let data_mask = 2u32.pow(data_size) - 1;
120
121 let discriminant = handle >> data_size;
123
124 let data = handle & data_mask;
126
127 Self::new(discriminant, data)
128 }
129
130 pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar {
131 #[expect(clippy::cast_possible_wrap)] let signed_handle = self.to_packed() as i32;
135 Scalar::from_target_isize(signed_handle.into(), cx)
136 }
137
138 pub fn try_from_scalar<'tcx>(
143 handle: Scalar,
144 cx: &MiriInterpCx<'tcx>,
145 ) -> InterpResult<'tcx, Result<Self, HandleError>> {
146 let sign_extended_handle = handle.to_target_isize(cx)?;
147
148 #[expect(clippy::cast_sign_loss)] let handle = if let Ok(signed_handle) = i32::try_from(sign_extended_handle) {
150 signed_handle as u32
151 } else {
152 return interp_ok(Err(HandleError::InvalidHandle));
154 };
155
156 match Self::from_packed(handle) {
157 Some(Self::Thread(thread)) => {
158 match cx.machine.threads.thread_id_try_from(thread.to_u32()) {
160 Ok(id) => interp_ok(Ok(Self::Thread(id))),
161 Err(e) => interp_ok(Err(HandleError::ThreadNotFound(e))),
162 }
163 }
164 Some(handle) => interp_ok(Ok(handle)),
165 None => interp_ok(Err(HandleError::InvalidHandle)),
166 }
167 }
168}
169
170impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
171
172#[allow(non_snake_case)]
173pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
174 fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'tcx, !> {
175 throw_machine_stop!(TerminationInfo::Abort(format!(
176 "invalid handle passed to `{function_name}`"
177 )))
178 }
179
180 fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
181 let this = self.eval_context_mut();
182
183 let handle = this.read_scalar(handle_op)?;
184 let ret = match Handle::try_from_scalar(handle, this)? {
185 Ok(Handle::Thread(thread)) => {
186 this.detach_thread(thread, true)?;
187 this.eval_windows("c", "TRUE")
188 }
189 _ => this.invalid_handle("CloseHandle")?,
190 };
191
192 interp_ok(ret)
193 }
194}