Skip to content

Commit 39cf83d

Browse files
committed
Refactor Handle slightly
- consistent handling, invalid handles are always an abort - Helper for reading handles that does the checking and machine stop - Use real handle types more places - Adds `File` and `Invalid` types of handle. File support will be added next
1 parent 7df496c commit 39cf83d

File tree

3 files changed

+78
-51
lines changed

3 files changed

+78
-51
lines changed

src/shims/windows/foreign_items.rs

+23-43
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,9 @@ use rustc_target::callconv::{Conv, FnAbi};
99

1010
use self::shims::windows::handle::{Handle, PseudoHandle};
1111
use crate::shims::os_str::bytes_to_os_str;
12-
use crate::shims::windows::handle::HandleError;
1312
use crate::shims::windows::*;
1413
use crate::*;
1514

16-
// The NTSTATUS STATUS_INVALID_HANDLE (0xC0000008) encoded as a HRESULT by setting the N bit.
17-
// (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a)
18-
const STATUS_INVALID_HANDLE: u32 = 0xD0000008;
19-
2015
pub fn is_dyn_sym(name: &str) -> bool {
2116
// std does dynamic detection for these symbols
2217
matches!(
@@ -498,52 +493,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
498493
"SetThreadDescription" => {
499494
let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?;
500495

501-
let handle = this.read_scalar(handle)?;
496+
let handle = this.read_handle(handle)?;
502497
let name = this.read_wide_str(this.read_pointer(name)?)?;
503498

504-
let thread = match Handle::try_from_scalar(handle, this)? {
505-
Ok(Handle::Thread(thread)) => Ok(thread),
506-
Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
507-
Ok(_) | Err(HandleError::InvalidHandle) =>
508-
this.invalid_handle("SetThreadDescription")?,
509-
Err(HandleError::ThreadNotFound(e)) => Err(e),
510-
};
511-
let res = match thread {
512-
Ok(thread) => {
513-
// FIXME: use non-lossy conversion
514-
this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
515-
Scalar::from_u32(0)
516-
}
517-
Err(_) => Scalar::from_u32(STATUS_INVALID_HANDLE),
499+
let thread = match handle {
500+
Handle::Thread(thread) => thread,
501+
Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
502+
_ => this.invalid_handle("SetThreadDescription")?,
518503
};
519-
520-
this.write_scalar(res, dest)?;
504+
// FIXME: use non-lossy conversion
505+
this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
506+
this.write_scalar(Scalar::from_u32(0), dest)?;
521507
}
522508
"GetThreadDescription" => {
523509
let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
524510

525-
let handle = this.read_scalar(handle)?;
511+
let handle = this.read_handle(handle)?;
526512
let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name
527513

528-
let thread = match Handle::try_from_scalar(handle, this)? {
529-
Ok(Handle::Thread(thread)) => Ok(thread),
530-
Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
531-
Ok(_) | Err(HandleError::InvalidHandle) =>
532-
this.invalid_handle("GetThreadDescription")?,
533-
Err(HandleError::ThreadNotFound(e)) => Err(e),
534-
};
535-
let (name, res) = match thread {
536-
Ok(thread) => {
537-
// Looks like the default thread name is empty.
538-
let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
539-
let name = this.alloc_os_str_as_wide_str(
540-
bytes_to_os_str(&name)?,
541-
MiriMemoryKind::WinLocal.into(),
542-
)?;
543-
(Scalar::from_maybe_pointer(name, this), Scalar::from_u32(0))
544-
}
545-
Err(_) => (Scalar::null_ptr(this), Scalar::from_u32(STATUS_INVALID_HANDLE)),
514+
let thread = match handle {
515+
Handle::Thread(thread) => thread,
516+
Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
517+
_ => this.invalid_handle("GetThreadDescription")?,
546518
};
519+
// Looks like the default thread name is empty.
520+
let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
521+
let name = this.alloc_os_str_as_wide_str(
522+
bytes_to_os_str(&name)?,
523+
MiriMemoryKind::WinLocal.into(),
524+
)?;
525+
let name = Scalar::from_maybe_pointer(name, this);
526+
let res = Scalar::from_u32(0);
547527

548528
this.write_scalar(name, &name_ptr)?;
549529
this.write_scalar(res, dest)?;
@@ -638,11 +618,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
638618
let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?;
639619
this.check_no_isolation("`GetModuleFileNameW`")?;
640620

641-
let handle = this.read_target_usize(handle)?;
621+
let handle = this.read_handle(handle)?;
642622
let filename = this.read_pointer(filename)?;
643623
let size = this.read_scalar(size)?.to_u32()?;
644624

645-
if handle != 0 {
625+
if handle != Handle::Null {
646626
throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
647627
}
648628

src/shims/windows/handle.rs

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::mem::variant_count;
2+
use std::panic::Location;
23

34
use rustc_abi::HasDataLayout;
45

@@ -16,6 +17,8 @@ pub enum Handle {
1617
Null,
1718
Pseudo(PseudoHandle),
1819
Thread(ThreadId),
20+
File(i32),
21+
Invalid,
1922
}
2023

2124
impl PseudoHandle {
@@ -47,12 +50,16 @@ impl Handle {
4750
const NULL_DISCRIMINANT: u32 = 0;
4851
const PSEUDO_DISCRIMINANT: u32 = 1;
4952
const THREAD_DISCRIMINANT: u32 = 2;
53+
const FILE_DISCRIMINANT: u32 = 3;
54+
const INVALID_DISCRIMINANT: u32 = 7;
5055

5156
fn discriminant(self) -> u32 {
5257
match self {
5358
Self::Null => Self::NULL_DISCRIMINANT,
5459
Self::Pseudo(_) => Self::PSEUDO_DISCRIMINANT,
5560
Self::Thread(_) => Self::THREAD_DISCRIMINANT,
61+
Self::File(_) => Self::FILE_DISCRIMINANT,
62+
Self::Invalid => Self::INVALID_DISCRIMINANT,
5663
}
5764
}
5865

@@ -61,11 +68,16 @@ impl Handle {
6168
Self::Null => 0,
6269
Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
6370
Self::Thread(thread) => thread.to_u32(),
71+
#[expect(clippy::cast_sign_loss)]
72+
Self::File(fd) => fd as u32,
73+
Self::Invalid => 0x1FFFFFFF,
6474
}
6575
}
6676

6777
fn packed_disc_size() -> u32 {
6878
// ceil(log2(x)) is how many bits it takes to store x numbers
79+
// We ensure that INVALID_HANDLE_VALUE (0xFFFFFFFF) decodes to Handle::Invalid
80+
// see https://devblogs.microsoft.com/oldnewthing/20230914-00/?p=108766
6981
let variant_count = variant_count::<Self>();
7082

7183
// however, std's ilog2 is floor(log2(x))
@@ -93,7 +105,7 @@ impl Handle {
93105
assert!(discriminant < 2u32.pow(disc_size));
94106

95107
// make sure the data fits into `data_size` bits
96-
assert!(data < 2u32.pow(data_size));
108+
assert!(data <= 2u32.pow(data_size));
97109

98110
// packs the data into the lower `data_size` bits
99111
// and packs the discriminant right above the data
@@ -105,6 +117,9 @@ impl Handle {
105117
Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
106118
Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
107119
Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))),
120+
#[expect(clippy::cast_possible_wrap)]
121+
Self::FILE_DISCRIMINANT => Some(Self::File(data as i32)),
122+
Self::INVALID_DISCRIMINANT => Some(Self::Invalid),
108123
_ => None,
109124
}
110125
}
@@ -171,6 +186,26 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
171186

172187
#[allow(non_snake_case)]
173188
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
189+
#[track_caller]
190+
fn read_handle(&self, handle: &OpTy<'tcx>) -> InterpResult<'tcx, Handle> {
191+
let this = self.eval_context_ref();
192+
let handle = this.read_scalar(handle)?;
193+
match Handle::try_from_scalar(handle, this)? {
194+
Ok(handle) => interp_ok(handle),
195+
Err(HandleError::InvalidHandle) =>
196+
throw_machine_stop!(TerminationInfo::Abort(format!(
197+
"invalid handle {} at {}",
198+
handle.to_target_isize(this)?,
199+
Location::caller(),
200+
))),
201+
Err(HandleError::ThreadNotFound(_)) =>
202+
throw_machine_stop!(TerminationInfo::Abort(format!(
203+
"invalid thread ID: {}",
204+
Location::caller()
205+
))),
206+
}
207+
}
208+
174209
fn invalid_handle(&mut self, function_name: &str) -> InterpResult<'tcx, !> {
175210
throw_machine_stop!(TerminationInfo::Abort(format!(
176211
"invalid handle passed to `{function_name}`"
@@ -180,12 +215,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
180215
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
181216
let this = self.eval_context_mut();
182217

183-
let handle = this.read_scalar(handle_op)?;
184-
let ret = match Handle::try_from_scalar(handle, this)? {
185-
Ok(Handle::Thread(thread)) => {
218+
let handle = this.read_handle(handle_op)?;
219+
let ret = match handle {
220+
Handle::Thread(thread) => {
186221
this.detach_thread(thread, /*allow_terminated_joined*/ true)?;
187222
this.eval_windows("c", "TRUE")
188223
}
224+
Handle::File(fd) =>
225+
if let Some(file) = this.machine.fds.get(fd) {
226+
let err = file.close(this.machine.communicate(), this)?;
227+
if let Err(e) = err {
228+
this.set_last_error(e)?;
229+
this.eval_windows("c", "FALSE")
230+
} else {
231+
this.eval_windows("c", "TRUE")
232+
}
233+
} else {
234+
this.invalid_handle("CloseHandle")?
235+
},
189236
_ => this.invalid_handle("CloseHandle")?,
190237
};
191238

src/shims/windows/thread.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
6262
) -> InterpResult<'tcx, Scalar> {
6363
let this = self.eval_context_mut();
6464

65-
let handle = this.read_scalar(handle_op)?;
65+
let handle = this.read_handle(handle_op)?;
6666
let timeout = this.read_scalar(timeout_op)?.to_u32()?;
6767

68-
let thread = match Handle::try_from_scalar(handle, this)? {
69-
Ok(Handle::Thread(thread)) => thread,
68+
let thread = match handle {
69+
Handle::Thread(thread) => thread,
7070
// Unlike on posix, the outcome of joining the current thread is not documented.
7171
// On current Windows, it just deadlocks.
72-
Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
72+
Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
7373
_ => this.invalid_handle("WaitForSingleObject")?,
7474
};
7575

0 commit comments

Comments
 (0)