miri/shims/windows/
sync.rs1use std::time::Duration;
2
3use rustc_abi::{FieldIdx, Size};
4
5use crate::concurrency::init_once::{EvalContextExt as _, InitOnceStatus};
6use crate::concurrency::sync::FutexRef;
7use crate::*;
8
9#[derive(Clone)]
10struct WindowsInitOnce {
11 init_once: InitOnceRef,
12}
13
14impl MetaObj for WindowsInitOnce {
15 fn on_access<'tcx>(&self) -> InterpResult<'tcx> {
16 if !self.init_once.queue_is_empty() {
17 throw_ub_format!(
18 "accessing the memory backing a `INIT_ONCE` is forbidden while the queue is non-empty"
19 );
20 }
21 interp_ok(())
22 }
23
24 fn opaque(&self) -> bool {
25 true
26 }
27}
28
29struct WindowsFutex {
30 futex: FutexRef,
31}
32
33impl MetaObj for WindowsFutex {
34 fn opaque(&self) -> bool {
35 false
36 }
37}
38
39impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
40trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
41 fn init_once_get_data<'a>(
45 &'a mut self,
46 init_once_ptr: &OpTy<'tcx>,
47 ) -> InterpResult<'tcx, &'a mut WindowsInitOnce>
48 where
49 'tcx: 'a,
50 {
51 let ecx = self.eval_context_mut();
52
53 let init_once = ecx.deref_pointer(init_once_ptr)?;
54
55 ecx.get_or_insert_meta(
56 &init_once,
57 |ecx| {
58 let ptr_field = ecx.project_field(&init_once, FieldIdx::from_u32(0))?;
59 let val = ecx.read_target_usize(&ptr_field)?;
60 if val == 0 {
61 interp_ok(WindowsInitOnce { init_once: InitOnceRef::new() })
62 } else {
63 throw_ub_format!("`INIT_ONCE` was not properly initialized at this location, or it got overwritten");
64 }
65 },
66 )
67 }
68
69 fn init_once_try_begin(
71 &mut self,
72 init_once_ref: &InitOnceRef,
73 pending_place: &MPlaceTy<'tcx>,
74 dest: &MPlaceTy<'tcx>,
75 ) -> InterpResult<'tcx, bool> {
76 let ecx = self.eval_context_mut();
77 interp_ok(match init_once_ref.status() {
78 InitOnceStatus::Uninitialized => {
79 init_once_ref.begin();
80 ecx.write_scalar(ecx.eval_windows("c", "TRUE"), pending_place)?;
81 ecx.write_scalar(ecx.eval_windows("c", "TRUE"), dest)?;
82 true
83 }
84 InitOnceStatus::Complete => {
85 ecx.init_once_observe_completed(init_once_ref)?;
86 ecx.write_scalar(ecx.eval_windows("c", "FALSE"), pending_place)?;
87 ecx.write_scalar(ecx.eval_windows("c", "TRUE"), dest)?;
88 true
89 }
90 InitOnceStatus::Begun => false,
91 })
92 }
93}
94
95impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
96#[allow(non_snake_case)]
97pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
98 fn InitOnceBeginInitialize(
99 &mut self,
100 init_once_op: &OpTy<'tcx>,
101 flags_op: &OpTy<'tcx>,
102 pending_op: &OpTy<'tcx>,
103 context_op: &OpTy<'tcx>,
104 dest: &MPlaceTy<'tcx>,
105 ) -> InterpResult<'tcx> {
106 let ecx = self.eval_context_mut();
107
108 let init_once = ecx.init_once_get_data(init_once_op)?.init_once.clone();
109 let flags = ecx.read_scalar(flags_op)?.to_u32()?;
110 let pending_place = ecx.deref_pointer(pending_op)?;
112 let context = ecx.read_pointer(context_op)?;
113
114 if flags != 0 {
115 throw_unsup_format!("unsupported `dwFlags` {flags} in `InitOnceBeginInitialize`");
116 }
117
118 if !ecx.ptr_is_null(context)? {
119 throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`");
120 }
121
122 if ecx.init_once_try_begin(&init_once, &pending_place, dest)? {
123 return interp_ok(());
125 }
126
127 let dest = dest.clone();
129 ecx.init_once_enqueue_and_block(
130 init_once.clone(),
131 callback!(
132 @capture<'tcx> {
133 init_once: InitOnceRef,
134 pending_place: MPlaceTy<'tcx>,
135 dest: MPlaceTy<'tcx>,
136 }
137 |ecx, unblock: UnblockKind| {
138 assert_eq!(unblock, UnblockKind::Ready);
139 let ret = ecx.init_once_try_begin(&init_once, &pending_place, &dest)?;
140 assert!(ret, "we were woken up but init_once_try_begin still failed");
141 interp_ok(())
142 }
143 ),
144 );
145 interp_ok(())
146 }
147
148 fn InitOnceComplete(
149 &mut self,
150 init_once_op: &OpTy<'tcx>,
151 flags_op: &OpTy<'tcx>,
152 context_op: &OpTy<'tcx>,
153 ) -> InterpResult<'tcx, Scalar> {
154 let ecx = self.eval_context_mut();
155
156 let init_once = ecx.init_once_get_data(init_once_op)?.init_once.clone();
157 let flags = ecx.read_scalar(flags_op)?.to_u32()?;
158 let context = ecx.read_pointer(context_op)?;
159
160 let success = if flags == 0 {
161 true
162 } else if flags == ecx.eval_windows_u32("c", "INIT_ONCE_INIT_FAILED") {
163 false
164 } else {
165 throw_unsup_format!("unsupported `dwFlags` {flags} in `InitOnceBeginInitialize`");
166 };
167
168 if !ecx.ptr_is_null(context)? {
169 throw_unsup_format!("non-null `lpContext` in `InitOnceBeginInitialize`");
170 }
171
172 if init_once.status() != InitOnceStatus::Begun {
173 throw_ub_format!(
175 "calling InitOnceComplete on a one time initialization that has not begun or is already completed"
176 );
177 }
178
179 if success {
180 ecx.init_once_complete(&init_once)?;
181 } else {
182 ecx.init_once_fail(&init_once)?;
183 }
184
185 interp_ok(ecx.eval_windows("c", "TRUE"))
186 }
187
188 fn WaitOnAddress(
189 &mut self,
190 ptr_op: &OpTy<'tcx>,
191 compare_op: &OpTy<'tcx>,
192 size_op: &OpTy<'tcx>,
193 timeout_op: &OpTy<'tcx>,
194 dest: &MPlaceTy<'tcx>,
195 ) -> InterpResult<'tcx> {
196 let ecx = self.eval_context_mut();
197
198 let ptr = ecx.read_pointer(ptr_op)?;
199 let compare = ecx.read_pointer(compare_op)?;
200 let size = ecx.read_target_usize(size_op)?;
201 let timeout_ms = ecx.read_scalar(timeout_op)?.to_u32()?;
202
203 if size > 8 || !size.is_power_of_two() {
204 let invalid_param = ecx.eval_windows("c", "ERROR_INVALID_PARAMETER");
205 ecx.set_last_error(invalid_param)?;
206 ecx.write_scalar(Scalar::from_i32(0), dest)?;
207 return interp_ok(());
208 };
209 let size = Size::from_bytes(size);
210
211 let deadline = if timeout_ms == ecx.eval_windows_u32("c", "INFINITE") {
212 None
213 } else {
214 let duration = Duration::from_millis(timeout_ms.into());
215 Some(ecx.machine.monotonic_clock.now().add_lossy(duration).into())
216 };
217
218 ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
220
221 let layout = ecx.machine.layouts.uint(size).unwrap();
222 let futex_place = ecx.ptr_to_mplace(ptr, layout);
223 let futex_val = ecx.read_scalar_atomic(&futex_place, AtomicReadOrd::Acquire)?;
224 let compare_val = ecx.read_scalar(&ecx.ptr_to_mplace(compare, layout))?;
225
226 if futex_val == compare_val {
227 let futex_ref = ecx
230 .get_or_insert_meta(&futex_place, |_| {
231 interp_ok(WindowsFutex { futex: Default::default() })
232 })?
233 .futex
234 .clone();
235
236 let dest = dest.clone();
237 ecx.futex_wait(
238 futex_ref,
239 u32::MAX, deadline,
241 callback!(
242 @capture<'tcx> {
243 dest: MPlaceTy<'tcx>
244 }
245 |ecx, unblock: UnblockKind| {
246 match unblock {
247 UnblockKind::Ready => {
248 ecx.write_int(1, &dest)
249 }
250 UnblockKind::TimedOut => {
251 ecx.set_last_error(IoError::WindowsError("ERROR_TIMEOUT"))?;
252 ecx.write_int(0, &dest)
253 }
254 }
255 }
256 ),
257 );
258 }
259
260 ecx.write_scalar(Scalar::from_i32(1), dest)?;
261
262 interp_ok(())
263 }
264
265 fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
266 let ecx = self.eval_context_mut();
267
268 let ptr = ecx.read_pointer(ptr_op)?;
269
270 ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
272
273 let Ok(futex) = ecx.try_get_meta::<WindowsFutex>(ptr) else {
274 return interp_ok(());
276 };
277 ecx.futex_wake(&futex.futex.clone(), u32::MAX, 1)?;
278
279 interp_ok(())
280 }
281 fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
282 let ecx = self.eval_context_mut();
283
284 let ptr = ecx.read_pointer(ptr_op)?;
285
286 ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
288
289 let Ok(futex) = ecx.try_get_meta::<WindowsFutex>(ptr) else {
290 return interp_ok(());
292 };
293 ecx.futex_wake(&futex.futex.clone(), u32::MAX, usize::MAX)?;
294
295 interp_ok(())
296 }
297}