Skip to main content

miri/shims/unix/macos/
foreign_items.rs

1use rustc_abi::CanonAbi;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5
6use super::sync::{EvalContextExt as _, MacOsFutexTimeout};
7use crate::shims::unix::*;
8use crate::*;
9
10pub fn is_dyn_sym(name: &str) -> bool {
11    match name {
12        // These only became available with macOS 11.0, so std looks them up dynamically.
13        "os_sync_wait_on_address"
14        | "os_sync_wait_on_address_with_deadline"
15        | "os_sync_wait_on_address_with_timeout"
16        | "os_sync_wake_by_address_any"
17        | "os_sync_wake_by_address_all" => true,
18        _ => false,
19    }
20}
21
22impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
23pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
24    fn emulate_foreign_item_inner(
25        &mut self,
26        link_name: Symbol,
27        abi: &FnAbi<'tcx, Ty<'tcx>>,
28        args: &[OpTy<'tcx>],
29        dest: &MPlaceTy<'tcx>,
30    ) -> InterpResult<'tcx, EmulateItemResult> {
31        let this = self.eval_context_mut();
32
33        // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
34
35        match link_name.as_str() {
36            // errno
37            "__error" => {
38                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
39                let errno_place = this.last_error_place()?;
40                this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
41            }
42
43            // File related shims
44            "close$NOCANCEL" => {
45                let [fd] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
46                let fd = this.read_scalar(fd)?.to_i32()?;
47                let result = this.close(fd)?;
48                this.write_scalar(result, dest)?;
49            }
50            "stat$INODE64" => {
51                let [path, buf] =
52                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
53                let result = this.stat(path, buf)?;
54                this.write_scalar(result, dest)?;
55            }
56            "lstat$INODE64" => {
57                let [path, buf] =
58                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
59                let result = this.lstat(path, buf)?;
60                this.write_scalar(result, dest)?;
61            }
62            "fstat$INODE64" => {
63                let [fd, buf] =
64                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
65                let result = this.fstat(fd, buf, "stat")?;
66                this.write_scalar(result, dest)?;
67            }
68            "fstatat$INODE64" => {
69                let [dirfd, path, buf, flags] = this.check_shim_sig(
70                    shim_sig!(extern "C" fn(i32, *_, *_, i32) -> i32),
71                    (link_name, abi, args),
72                )?;
73                let result = this.fstatat(dirfd, path, buf, flags)?;
74                this.write_scalar(result, dest)?;
75            }
76            "opendir$INODE64" => {
77                let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
78                let result = this.opendir(name)?;
79                this.write_scalar(result, dest)?;
80            }
81            "readdir$INODE64" => {
82                let [dirp] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
83                this.readdir(dirp, dest, "dirent")?;
84            }
85            "realpath$DARWIN_EXTSN" => {
86                let [path, resolved_path] =
87                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
88                let result = this.realpath(path, resolved_path)?;
89                this.write_scalar(result, dest)?;
90            }
91
92            // Environment related shims
93            "_NSGetEnviron" => {
94                // FIXME: This does not have a direct test (#3179).
95                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
96                let environ = this.machine.env_vars.unix().environ();
97                this.write_pointer(environ, dest)?;
98            }
99
100            // Random data generation
101            "CCRandomGenerateBytes" => {
102                let [bytes, count] =
103                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
104                let bytes = this.read_pointer(bytes)?;
105                let count = this.read_target_usize(count)?;
106                let success = this.eval_libc_i32("kCCSuccess");
107                this.gen_random(bytes, count)?;
108                this.write_int(success, dest)?;
109            }
110
111            // Time related shims
112            "mach_absolute_time" => {
113                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
114                let result = this.mach_absolute_time()?;
115                this.write_scalar(result, dest)?;
116            }
117
118            "mach_timebase_info" => {
119                // FIXME: This does not have a direct test (#3179).
120                let [info] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
121                let result = this.mach_timebase_info(info)?;
122                this.write_scalar(result, dest)?;
123            }
124
125            "mach_wait_until" => {
126                // FIXME: This does not have a direct test (#3179).
127                let [deadline] =
128                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
129                let result = this.mach_wait_until(deadline)?;
130                this.write_scalar(result, dest)?;
131            }
132
133            // Access to command-line arguments
134            "_NSGetArgc" => {
135                // FIXME: This does not have a direct test (#3179).
136                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
137                this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
138            }
139            "_NSGetArgv" => {
140                // FIXME: This does not have a direct test (#3179).
141                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
142                this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
143            }
144            "_NSGetExecutablePath" => {
145                // FIXME: This does not have a direct test (#3179).
146                let [buf, bufsize] =
147                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
148                this.check_no_isolation("`_NSGetExecutablePath`")?;
149
150                let buf_ptr = this.read_pointer(buf)?;
151                let bufsize = this.deref_pointer_as(bufsize, this.machine.layouts.u32)?;
152
153                let Some(path) = this.machine.current_exe.clone() else {
154                    throw_unsup_format!(
155                        "_NSGetExecutablePath only works when Miri is run on a file"
156                    )
157                };
158                let (written, size_needed) = this.write_path_to_c_str(
159                    &path,
160                    buf_ptr,
161                    this.read_scalar(&bufsize)?.to_u32()?.into(),
162                )?;
163
164                if written {
165                    this.write_null(dest)?;
166                } else {
167                    this.write_scalar(Scalar::from_u32(size_needed.try_into().unwrap()), &bufsize)?;
168                    this.write_int(-1, dest)?;
169                }
170            }
171
172            // Thread-local storage
173            "_tlv_atexit" => {
174                let [dtor, data] =
175                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
176                let dtor = this.read_pointer(dtor)?;
177                let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
178                let data = this.read_scalar(data)?;
179                let active_thread = this.active_thread();
180                this.machine.tls.add_macos_thread_dtor(
181                    active_thread,
182                    dtor,
183                    data,
184                    this.machine.current_user_relevant_span(),
185                )?;
186            }
187
188            // Querying system information
189            "pthread_get_stackaddr_np" => {
190                // FIXME: This does not have a direct test (#3179).
191                let [thread] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
192                this.read_target_usize(thread)?;
193                let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
194                this.write_scalar(stack_addr, dest)?;
195            }
196            "pthread_get_stacksize_np" => {
197                // FIXME: This does not have a direct test (#3179).
198                let [thread] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
199                this.read_target_usize(thread)?;
200                let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
201                this.write_scalar(stack_size, dest)?;
202            }
203
204            // Threading
205            "pthread_setname_np" => {
206                let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
207
208                // The real implementation has logic in two places:
209                // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
210                // * in kernel at https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/proc_info.c#L3218-L3227.
211                //
212                // The function in libc calls the kernel to validate
213                // the security policies and the input. If all of the requirements
214                // are met, then the name is set and 0 is returned. Otherwise, if
215                // the specified name is lomnger than MAXTHREADNAMESIZE, then
216                // ENAMETOOLONG is returned.
217                let thread = this.pthread_self()?;
218                let res = match this.pthread_setname_np(
219                    thread,
220                    this.read_scalar(name)?,
221                    this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?,
222                    /* truncate */ false,
223                )? {
224                    ThreadNameResult::Ok => Scalar::from_i32(0),
225                    ThreadNameResult::NameTooLong => {
226                        // Testing on native systems indicates that the error is returned via errno.
227                        this.set_errno_and_return_neg1_i32(LibcError("ENAMETOOLONG"))?
228                    }
229                };
230                // Contrary to the manpage, `pthread_setname_np` on macOS still
231                // returns an integer indicating success.
232                this.write_scalar(res, dest)?;
233            }
234            "pthread_getname_np" => {
235                let [thread, name, len] =
236                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
237
238                // The function's behavior isn't portable between platforms.
239                // In case of macOS, a truncated name (due to a too small buffer)
240                // does not lead to an error.
241                //
242                // For details, see the implementation at
243                // https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175.
244                // The key part is the strlcpy, which truncates the resulting value,
245                // but always null terminates (except for zero sized buffers).
246                let res = match this.pthread_getname_np(
247                    this.read_scalar(thread)?,
248                    this.read_scalar(name)?,
249                    this.read_scalar(len)?,
250                    /* truncate */ true,
251                )? {
252                    ThreadNameResult::Ok => Scalar::from_i32(0),
253                    // `NameTooLong` is possible when the buffer is zero sized,
254                    ThreadNameResult::NameTooLong => Scalar::from_i32(0),
255                };
256                this.write_scalar(res, dest)?;
257            }
258            "pthread_threadid_np" => {
259                let [thread, tid_ptr] =
260                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
261                let res = this.apple_pthread_threadid_np(thread, tid_ptr)?;
262                this.write_scalar(res, dest)?;
263            }
264
265            // Synchronization primitives
266            "os_sync_wait_on_address" => {
267                let [addr_op, value_op, size_op, flags_op] =
268                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
269                this.os_sync_wait_on_address(
270                    addr_op,
271                    value_op,
272                    size_op,
273                    flags_op,
274                    MacOsFutexTimeout::None,
275                    dest,
276                )?;
277            }
278            "os_sync_wait_on_address_with_deadline" => {
279                let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
280                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
281                this.os_sync_wait_on_address(
282                    addr_op,
283                    value_op,
284                    size_op,
285                    flags_op,
286                    MacOsFutexTimeout::Absolute { clock_op, timeout_op },
287                    dest,
288                )?;
289            }
290            "os_sync_wait_on_address_with_timeout" => {
291                let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
292                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
293                this.os_sync_wait_on_address(
294                    addr_op,
295                    value_op,
296                    size_op,
297                    flags_op,
298                    MacOsFutexTimeout::Relative { clock_op, timeout_op },
299                    dest,
300                )?;
301            }
302            "os_sync_wake_by_address_any" => {
303                let [addr_op, size_op, flags_op] =
304                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
305                this.os_sync_wake_by_address(
306                    addr_op, size_op, flags_op, /* all */ false, dest,
307                )?;
308            }
309            "os_sync_wake_by_address_all" => {
310                let [addr_op, size_op, flags_op] =
311                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
312                this.os_sync_wake_by_address(
313                    addr_op, size_op, flags_op, /* all */ true, dest,
314                )?;
315            }
316            "os_unfair_lock_lock" => {
317                let [lock_op] =
318                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
319                this.os_unfair_lock_lock(lock_op)?;
320            }
321            "os_unfair_lock_trylock" => {
322                let [lock_op] =
323                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
324                this.os_unfair_lock_trylock(lock_op, dest)?;
325            }
326            "os_unfair_lock_unlock" => {
327                let [lock_op] =
328                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
329                this.os_unfair_lock_unlock(lock_op)?;
330            }
331            "os_unfair_lock_assert_owner" => {
332                let [lock_op] =
333                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
334                this.os_unfair_lock_assert_owner(lock_op)?;
335            }
336            "os_unfair_lock_assert_not_owner" => {
337                let [lock_op] =
338                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
339                this.os_unfair_lock_assert_not_owner(lock_op)?;
340            }
341            "dispatch_semaphore_create" => {
342                let [value] = this.check_shim_sig(
343                    shim_sig!(extern "C" fn(isize) -> *_),
344                    (link_name, abi, args),
345                )?;
346                this.dispatch_semaphore_create(value, dest)?;
347            }
348            "dispatch_semaphore_wait" => {
349                let [sem, timeout] = this.check_shim_sig(
350                    shim_sig!(extern "C" fn(*_, u64) -> isize),
351                    (link_name, abi, args),
352                )?;
353                this.dispatch_semaphore_wait(sem, timeout, dest)?;
354            }
355            "dispatch_semaphore_signal" => {
356                let [sem] = this.check_shim_sig(
357                    shim_sig!(extern "C" fn(*_) -> isize),
358                    (link_name, abi, args),
359                )?;
360                this.dispatch_semaphore_signal(sem, dest)?;
361            }
362            "dispatch_release" => {
363                let [sem] = this
364                    .check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), (link_name, abi, args))?;
365                this.dispatch_release(sem)?;
366            }
367            "dispatch_time" => {
368                let [when, delta] = this.check_shim_sig(
369                    shim_sig!(extern "C" fn(u64, i64) -> u64),
370                    (link_name, abi, args),
371                )?;
372                this.dispatch_time(when, delta, dest)?;
373            }
374
375            "pthread_cond_timedwait_relative_np" => {
376                let [cond, mutex, reltime] =
377                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
378                this.pthread_cond_timedwait(
379                    cond, mutex, reltime, dest, /* macos_relative_np */ true,
380                )?;
381            }
382
383            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
384            // These shims are enabled only when the caller is in the standard library.
385            "confstr" => {
386                let [_key, _buf, _buflen] =
387                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
388                // We just pretend that no configuration key exists, and return EINVAL.
389                this.set_last_error(LibcError("EINVAL"))?;
390                this.write_null(dest)?;
391            }
392
393            _ => return interp_ok(EmulateItemResult::NotSupported),
394        };
395
396        interp_ok(EmulateItemResult::NeedsReturn)
397    }
398}