]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/src/shims/windows/foreign_items.rs
05ce81b71a47dcd8a80872920ea0b09f761a4a90
[rust.git] / src / tools / miri / src / shims / windows / foreign_items.rs
1 use std::iter;
2
3 use rustc_span::Symbol;
4 use rustc_target::abi::Size;
5 use rustc_target::spec::abi::Abi;
6
7 use crate::*;
8 use shims::foreign_items::EmulateByNameResult;
9 use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
10 use shims::windows::sync::EvalContextExt as _;
11 use shims::windows::thread::EvalContextExt as _;
12
13 use smallvec::SmallVec;
14
15 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
16 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
17     fn emulate_foreign_item_by_name(
18         &mut self,
19         link_name: Symbol,
20         abi: Abi,
21         args: &[OpTy<'tcx, Provenance>],
22         dest: &PlaceTy<'tcx, Provenance>,
23     ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
24         let this = self.eval_context_mut();
25
26         // See `fn emulate_foreign_item_by_name` in `shims/foreign_items.rs` for the general pattern.
27
28         // Windows API stubs.
29         // HANDLE = isize
30         // NTSTATUS = LONH = i32
31         // DWORD = ULONG = u32
32         // BOOL = i32
33         // BOOLEAN = u8
34         match link_name.as_str() {
35             // Environment related shims
36             "GetEnvironmentVariableW" => {
37                 let [name, buf, size] =
38                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
39                 let result = this.GetEnvironmentVariableW(name, buf, size)?;
40                 this.write_scalar(result, dest)?;
41             }
42             "SetEnvironmentVariableW" => {
43                 let [name, value] =
44                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
45                 let result = this.SetEnvironmentVariableW(name, value)?;
46                 this.write_scalar(result, dest)?;
47             }
48             "GetEnvironmentStringsW" => {
49                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
50                 let result = this.GetEnvironmentStringsW()?;
51                 this.write_pointer(result, dest)?;
52             }
53             "FreeEnvironmentStringsW" => {
54                 let [env_block] =
55                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
56                 let result = this.FreeEnvironmentStringsW(env_block)?;
57                 this.write_scalar(result, dest)?;
58             }
59             "GetCurrentDirectoryW" => {
60                 let [size, buf] =
61                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
62                 let result = this.GetCurrentDirectoryW(size, buf)?;
63                 this.write_scalar(result, dest)?;
64             }
65             "SetCurrentDirectoryW" => {
66                 let [path] =
67                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
68                 let result = this.SetCurrentDirectoryW(path)?;
69                 this.write_scalar(result, dest)?;
70             }
71
72             // Allocation
73             "HeapAlloc" => {
74                 let [handle, flags, size] =
75                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
76                 this.read_scalar(handle)?.to_machine_isize(this)?;
77                 let flags = this.read_scalar(flags)?.to_u32()?;
78                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
79                 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
80                 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
81                 this.write_pointer(res, dest)?;
82             }
83             "HeapFree" => {
84                 let [handle, flags, ptr] =
85                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
86                 this.read_scalar(handle)?.to_machine_isize(this)?;
87                 this.read_scalar(flags)?.to_u32()?;
88                 let ptr = this.read_pointer(ptr)?;
89                 this.free(ptr, MiriMemoryKind::WinHeap)?;
90                 this.write_scalar(Scalar::from_i32(1), dest)?;
91             }
92             "HeapReAlloc" => {
93                 let [handle, flags, ptr, size] =
94                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
95                 this.read_scalar(handle)?.to_machine_isize(this)?;
96                 this.read_scalar(flags)?.to_u32()?;
97                 let ptr = this.read_pointer(ptr)?;
98                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
99                 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
100                 this.write_pointer(res, dest)?;
101             }
102
103             // errno
104             "SetLastError" => {
105                 let [error] =
106                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
107                 let error = this.read_scalar(error)?;
108                 this.set_last_error(error)?;
109             }
110             "GetLastError" => {
111                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
112                 let last_error = this.get_last_error()?;
113                 this.write_scalar(last_error, dest)?;
114             }
115
116             // Querying system information
117             "GetSystemInfo" => {
118                 // Also called from `page_size` crate.
119                 let [system_info] =
120                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
121                 let system_info = this.deref_operand(system_info)?;
122                 // Initialize with `0`.
123                 this.write_bytes_ptr(
124                     system_info.ptr,
125                     iter::repeat(0u8).take(system_info.layout.size.bytes_usize()),
126                 )?;
127                 // Set selected fields.
128                 let word_layout = this.machine.layouts.u16;
129                 let dword_layout = this.machine.layouts.u32;
130                 let usize_layout = this.machine.layouts.usize;
131
132                 // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
133                 // Pointer fields have different sizes on different targets.
134                 // To avoid all these issue we calculate the offsets ourselves.
135                 let field_sizes = [
136                     word_layout.size,  // 0,  wProcessorArchitecture      : WORD
137                     word_layout.size,  // 1,  wReserved                   : WORD
138                     dword_layout.size, // 2,  dwPageSize                  : DWORD
139                     usize_layout.size, // 3,  lpMinimumApplicationAddress : LPVOID
140                     usize_layout.size, // 4,  lpMaximumApplicationAddress : LPVOID
141                     usize_layout.size, // 5,  dwActiveProcessorMask       : DWORD_PTR
142                     dword_layout.size, // 6,  dwNumberOfProcessors        : DWORD
143                     dword_layout.size, // 7,  dwProcessorType             : DWORD
144                     dword_layout.size, // 8,  dwAllocationGranularity     : DWORD
145                     word_layout.size,  // 9,  wProcessorLevel             : WORD
146                     word_layout.size,  // 10, wProcessorRevision          : WORD
147                 ];
148                 let field_offsets: SmallVec<[Size; 11]> = field_sizes
149                     .iter()
150                     .copied()
151                     .scan(Size::ZERO, |a, x| {
152                         let res = Some(*a);
153                         *a += x;
154                         res
155                     })
156                     .collect();
157
158                 // Set page size.
159                 let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?;
160                 this.write_scalar(
161                     Scalar::from_int(this.machine.page_size, dword_layout.size),
162                     &page_size.into(),
163                 )?;
164                 // Set number of processors.
165                 let num_cpus = system_info.offset(field_offsets[6], dword_layout, &this.tcx)?;
166                 this.write_scalar(
167                     Scalar::from_int(this.machine.num_cpus, dword_layout.size),
168                     &num_cpus.into(),
169                 )?;
170             }
171
172             // Thread-local storage
173             "TlsAlloc" => {
174                 // This just creates a key; Windows does not natively support TLS destructors.
175
176                 // Create key and return it.
177                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
178                 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
179                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
180             }
181             "TlsGetValue" => {
182                 let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
183                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
184                 let active_thread = this.get_active_thread();
185                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
186                 this.write_scalar(ptr, dest)?;
187             }
188             "TlsSetValue" => {
189                 let [key, new_ptr] =
190                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
191                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
192                 let active_thread = this.get_active_thread();
193                 let new_data = this.read_scalar(new_ptr)?;
194                 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
195
196                 // Return success (`1`).
197                 this.write_scalar(Scalar::from_i32(1), dest)?;
198             }
199
200             // Access to command-line arguments
201             "GetCommandLineW" => {
202                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
203                 this.write_pointer(
204                     this.machine.cmd_line.expect("machine must be initialized").ptr,
205                     dest,
206                 )?;
207             }
208
209             // Time related shims
210             "GetSystemTimeAsFileTime" => {
211                 #[allow(non_snake_case)]
212                 let [LPFILETIME] =
213                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
214                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
215             }
216             "QueryPerformanceCounter" => {
217                 #[allow(non_snake_case)]
218                 let [lpPerformanceCount] =
219                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
220                 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
221                 this.write_scalar(result, dest)?;
222             }
223             "QueryPerformanceFrequency" => {
224                 #[allow(non_snake_case)]
225                 let [lpFrequency] =
226                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
227                 let result = this.QueryPerformanceFrequency(lpFrequency)?;
228                 this.write_scalar(result, dest)?;
229             }
230             "Sleep" => {
231                 let [timeout] =
232                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
233
234                 this.Sleep(timeout)?;
235             }
236
237             // Synchronization primitives
238             "AcquireSRWLockExclusive" => {
239                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
240                 this.AcquireSRWLockExclusive(ptr)?;
241             }
242             "ReleaseSRWLockExclusive" => {
243                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
244                 this.ReleaseSRWLockExclusive(ptr)?;
245             }
246             "TryAcquireSRWLockExclusive" => {
247                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
248                 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
249                 this.write_scalar(ret, dest)?;
250             }
251             "AcquireSRWLockShared" => {
252                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
253                 this.AcquireSRWLockShared(ptr)?;
254             }
255             "ReleaseSRWLockShared" => {
256                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
257                 this.ReleaseSRWLockShared(ptr)?;
258             }
259             "TryAcquireSRWLockShared" => {
260                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
261                 let ret = this.TryAcquireSRWLockShared(ptr)?;
262                 this.write_scalar(ret, dest)?;
263             }
264             "InitOnceBeginInitialize" => {
265                 let [ptr, flags, pending, context] =
266                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
267                 let result = this.InitOnceBeginInitialize(ptr, flags, pending, context)?;
268                 this.write_scalar(result, dest)?;
269             }
270             "InitOnceComplete" => {
271                 let [ptr, flags, context] =
272                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
273                 let result = this.InitOnceComplete(ptr, flags, context)?;
274                 this.write_scalar(result, dest)?;
275             }
276             "SleepConditionVariableSRW" => {
277                 let [condvar, lock, timeout, flags] =
278                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
279
280                 let result = this.SleepConditionVariableSRW(condvar, lock, timeout, flags, dest)?;
281                 this.write_scalar(result, dest)?;
282             }
283             "WakeConditionVariable" => {
284                 let [condvar] =
285                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
286
287                 this.WakeConditionVariable(condvar)?;
288             }
289             "WakeAllConditionVariable" => {
290                 let [condvar] =
291                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
292
293                 this.WakeAllConditionVariable(condvar)?;
294             }
295
296             // Dynamic symbol loading
297             "GetProcAddress" => {
298                 #[allow(non_snake_case)]
299                 let [hModule, lpProcName] =
300                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
301                 this.read_scalar(hModule)?.to_machine_isize(this)?;
302                 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
303                 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
304                     let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
305                     this.write_pointer(ptr, dest)?;
306                 } else {
307                     this.write_null(dest)?;
308                 }
309             }
310
311             // Miscellaneous
312             "SystemFunction036" => {
313                 // This is really 'RtlGenRandom'.
314                 let [ptr, len] =
315                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
316                 let ptr = this.read_pointer(ptr)?;
317                 let len = this.read_scalar(len)?.to_u32()?;
318                 this.gen_random(ptr, len.into())?;
319                 this.write_scalar(Scalar::from_bool(true), dest)?;
320             }
321             "BCryptGenRandom" => {
322                 let [algorithm, ptr, len, flags] =
323                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
324                 let algorithm = this.read_scalar(algorithm)?;
325                 let algorithm = algorithm.to_machine_usize(this)?;
326                 let ptr = this.read_pointer(ptr)?;
327                 let len = this.read_scalar(len)?.to_u32()?;
328                 let flags = this.read_scalar(flags)?.to_u32()?;
329                 match flags {
330                     0 => {
331                         if algorithm != 0x81 {
332                             // BCRYPT_RNG_ALG_HANDLE
333                             throw_unsup_format!(
334                                 "BCryptGenRandom algorithm must be BCRYPT_RNG_ALG_HANDLE when the flag is 0"
335                             );
336                         }
337                     }
338                     2 => {
339                         // BCRYPT_USE_SYSTEM_PREFERRED_RNG
340                         if algorithm != 0 {
341                             throw_unsup_format!(
342                                 "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
343                             );
344                         }
345                     }
346                     _ => {
347                         throw_unsup_format!(
348                             "BCryptGenRandom is only supported with BCRYPT_USE_SYSTEM_PREFERRED_RNG or BCRYPT_RNG_ALG_HANDLE"
349                         );
350                     }
351                 }
352                 this.gen_random(ptr, len.into())?;
353                 this.write_null(dest)?; // STATUS_SUCCESS
354             }
355             "GetConsoleScreenBufferInfo" => {
356                 // `term` needs this, so we fake it.
357                 let [console, buffer_info] =
358                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
359                 this.read_scalar(console)?.to_machine_isize(this)?;
360                 this.deref_operand(buffer_info)?;
361                 // Indicate an error.
362                 // FIXME: we should set last_error, but to what?
363                 this.write_null(dest)?;
364             }
365             "GetStdHandle" => {
366                 let [which] =
367                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
368                 let which = this.read_scalar(which)?.to_i32()?;
369                 // We just make this the identity function, so we know later in `NtWriteFile` which
370                 // one it is. This is very fake, but libtest needs it so we cannot make it a
371                 // std-only shim.
372                 // FIXME: this should return real HANDLEs when io support is added
373                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
374             }
375             "CloseHandle" => {
376                 let [handle] =
377                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
378
379                 this.CloseHandle(handle)?;
380
381                 this.write_scalar(Scalar::from_u32(1), dest)?;
382             }
383
384             // Threading
385             "CreateThread" => {
386                 let [security, stacksize, start, arg, flags, thread] =
387                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
388
389                 let thread_id =
390                     this.CreateThread(security, stacksize, start, arg, flags, thread)?;
391
392                 this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
393             }
394             "WaitForSingleObject" => {
395                 let [handle, timeout] =
396                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
397
398                 let ret = this.WaitForSingleObject(handle, timeout)?;
399                 this.write_scalar(Scalar::from_u32(ret), dest)?;
400             }
401             "GetCurrentThread" => {
402                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
403
404                 this.write_scalar(
405                     Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
406                     dest,
407                 )?;
408             }
409
410             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
411             // These shims are enabled only when the caller is in the standard library.
412             "GetProcessHeap" if this.frame_in_std() => {
413                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
414                 // Just fake a HANDLE
415                 // It's fine to not use the Handle type here because its a stub
416                 this.write_int(1, dest)?;
417             }
418             "GetModuleHandleA" if this.frame_in_std() => {
419                 #[allow(non_snake_case)]
420                 let [_lpModuleName] =
421                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
422                 // We need to return something non-null here to make `compat_fn!` work.
423                 this.write_int(1, dest)?;
424             }
425             "SetConsoleTextAttribute" if this.frame_in_std() => {
426                 #[allow(non_snake_case)]
427                 let [_hConsoleOutput, _wAttribute] =
428                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
429                 // Pretend these does not exist / nothing happened, by returning zero.
430                 this.write_null(dest)?;
431             }
432             "GetConsoleMode" if this.frame_in_std() => {
433                 let [console, mode] =
434                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
435                 this.read_scalar(console)?.to_machine_isize(this)?;
436                 this.deref_operand(mode)?;
437                 // Indicate an error.
438                 this.write_null(dest)?;
439             }
440             "GetFileType" if this.frame_in_std() => {
441                 #[allow(non_snake_case)]
442                 let [_hFile] =
443                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
444                 // Return unknown file type.
445                 this.write_null(dest)?;
446             }
447             "AddVectoredExceptionHandler" if this.frame_in_std() => {
448                 #[allow(non_snake_case)]
449                 let [_First, _Handler] =
450                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
451                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
452                 this.write_int(1, dest)?;
453             }
454             "SetThreadStackGuarantee" if this.frame_in_std() => {
455                 #[allow(non_snake_case)]
456                 let [_StackSizeInBytes] =
457                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
458                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
459                 this.write_int(1, dest)?;
460             }
461             "GetCurrentProcessId" if this.frame_in_std() => {
462                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
463                 let result = this.GetCurrentProcessId()?;
464                 this.write_int(result, dest)?;
465             }
466             // this is only callable from std because we know that std ignores the return value
467             "SwitchToThread" if this.frame_in_std() => {
468                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
469
470                 this.yield_active_thread();
471
472                 // FIXME: this should return a nonzero value if this call does result in switching to another thread.
473                 this.write_null(dest)?;
474             }
475
476             _ => return Ok(EmulateByNameResult::NotSupported),
477         }
478
479         Ok(EmulateByNameResult::NeedsJumping)
480     }
481 }