miri/shims/windows/
handle.rs1use std::mem::variant_count;
2
3use rustc_abi::HasDataLayout;
4
5use crate::concurrency::thread::ThreadNotFound;
6use crate::shims::files::FdNum;
7use crate::*;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub enum PseudoHandle {
11 CurrentThread,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub enum Handle {
17 Null,
18 Pseudo(PseudoHandle),
19 Thread(ThreadId),
20 File(FdNum),
21 Invalid,
22}
23
24impl PseudoHandle {
25 const CURRENT_THREAD_VALUE: u32 = 0;
26
27 fn value(self) -> u32 {
28 match self {
29 Self::CurrentThread => Self::CURRENT_THREAD_VALUE,
30 }
31 }
32
33 fn from_value(value: u32) -> Option<Self> {
34 match value {
35 Self::CURRENT_THREAD_VALUE => Some(Self::CurrentThread),
36 _ => None,
37 }
38 }
39}
40
41pub enum HandleError {
43 ThreadNotFound(ThreadNotFound),
45 InvalidHandle,
47}
48
49impl Handle {
50 const NULL_DISCRIMINANT: u32 = 0;
51 const PSEUDO_DISCRIMINANT: u32 = 1;
52 const THREAD_DISCRIMINANT: u32 = 2;
53 const FILE_DISCRIMINANT: u32 = 3;
54 const INVALID_DISCRIMINANT: u32 = 7;
57
58 fn discriminant(self) -> u32 {
59 match self {
60 Self::Null => Self::NULL_DISCRIMINANT,
61 Self::Pseudo(_) => Self::PSEUDO_DISCRIMINANT,
62 Self::Thread(_) => Self::THREAD_DISCRIMINANT,
63 Self::File(_) => Self::FILE_DISCRIMINANT,
64 Self::Invalid => Self::INVALID_DISCRIMINANT,
65 }
66 }
67
68 fn data(self) -> u32 {
69 match self {
70 Self::Null => 0,
71 Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
72 Self::Thread(thread) => thread.to_u32(),
73 #[expect(clippy::cast_sign_loss)]
74 Self::File(fd) => fd as u32,
75 Self::Invalid => 0x1FFFFFFF,
80 }
81 }
82
83 fn packed_disc_size() -> u32 {
84 let variant_count = variant_count::<Self>();
89
90 let floor_log2 = variant_count.ilog2();
92
93 #[expect(clippy::arithmetic_side_effects)] if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 }
96 }
97
98 fn to_packed(self) -> u32 {
105 let disc_size = Self::packed_disc_size();
106 let data_size = u32::BITS.strict_sub(disc_size);
107
108 let discriminant = self.discriminant();
109 let data = self.data();
110
111 assert!(discriminant < 2u32.pow(disc_size));
113
114 assert!(data < 2u32.pow(data_size));
116
117 (discriminant << data_size) | data
120 }
121
122 fn new(discriminant: u32, data: u32) -> Option<Self> {
123 match discriminant {
124 Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
125 Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
126 Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))),
127 #[expect(clippy::cast_possible_wrap)]
128 Self::FILE_DISCRIMINANT => {
129 assert_eq!(size_of_val(&data), size_of::<FdNum>());
131 Some(Self::File(data as FdNum))
132 }
133 Self::INVALID_DISCRIMINANT => Some(Self::Invalid),
134 _ => None,
135 }
136 }
137
138 fn from_packed(handle: u32) -> Option<Self> {
140 let disc_size = Self::packed_disc_size();
141 let data_size = u32::BITS.strict_sub(disc_size);
142
143 #[expect(clippy::arithmetic_side_effects)] let data_mask = 2u32.pow(data_size) - 1;
146
147 let discriminant = handle >> data_size;
149
150 let data = handle & data_mask;
152
153 Self::new(discriminant, data)
154 }
155
156 pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar {
157 #[expect(clippy::cast_possible_wrap)] let signed_handle = self.to_packed() as i32;
161 Scalar::from_target_isize(signed_handle.into(), cx)
162 }
163
164 fn try_from_scalar<'tcx>(
169 handle: Scalar,
170 cx: &MiriInterpCx<'tcx>,
171 ) -> InterpResult<'tcx, Result<Self, HandleError>> {
172 let sign_extended_handle = handle.to_target_isize(cx)?;
173
174 #[expect(clippy::cast_sign_loss)] let handle = if let Ok(signed_handle) = i32::try_from(sign_extended_handle) {
176 signed_handle as u32
177 } else {
178 return interp_ok(Err(HandleError::InvalidHandle));
180 };
181
182 match Self::from_packed(handle) {
183 Some(Self::Thread(thread)) => {
184 match cx.machine.threads.thread_id_try_from(thread.to_u32()) {
186 Ok(id) => interp_ok(Ok(Self::Thread(id))),
187 Err(e) => interp_ok(Err(HandleError::ThreadNotFound(e))),
188 }
189 }
190 Some(handle) => interp_ok(Ok(handle)),
191 None => interp_ok(Err(HandleError::InvalidHandle)),
192 }
193 }
194}
195
196impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
197
198#[allow(non_snake_case)]
199pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
200 #[track_caller]
203 fn read_handle(&self, handle: &OpTy<'tcx>, function_name: &str) -> InterpResult<'tcx, Handle> {
204 let this = self.eval_context_ref();
205 let handle = this.read_scalar(handle)?;
206 match Handle::try_from_scalar(handle, this)? {
207 Ok(handle) => interp_ok(handle),
208 Err(HandleError::InvalidHandle) =>
209 throw_machine_stop!(TerminationInfo::Abort(format!(
210 "invalid handle {} passed to {function_name}",
211 handle.to_target_isize(this)?,
212 ))),
213 Err(HandleError::ThreadNotFound(_)) =>
214 throw_machine_stop!(TerminationInfo::Abort(format!(
215 "invalid thread ID {} passed to {function_name}",
216 handle.to_target_isize(this)?,
217 ))),
218 }
219 }
220
221 fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'tcx, !> {
222 throw_machine_stop!(TerminationInfo::Abort(format!(
223 "invalid handle passed to `{function_name}`"
224 )))
225 }
226
227 fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
228 let this = self.eval_context_mut();
229
230 let handle = this.read_handle(handle_op, "CloseHandle")?;
231 let ret = match handle {
232 Handle::Thread(thread) => {
233 this.detach_thread(thread, true)?;
234 this.eval_windows("c", "TRUE")
235 }
236 Handle::File(fd_num) =>
237 if let Some(fd) = this.machine.fds.remove(fd_num) {
238 let err = fd.close_ref(this.machine.communicate(), this)?;
239 if let Err(e) = err {
240 this.set_last_error(e)?;
241 this.eval_windows("c", "FALSE")
242 } else {
243 this.eval_windows("c", "TRUE")
244 }
245 } else {
246 this.invalid_handle("CloseHandle")?
247 },
248 _ => this.invalid_handle("CloseHandle")?,
249 };
250
251 interp_ok(ret)
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn test_invalid_encoding() {
261 assert_eq!(Handle::Invalid.to_packed(), u32::MAX)
263 }
264}