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)?;
66                this.write_scalar(result, dest)?;
67            }
68            "opendir$INODE64" => {
69                let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
70                let result = this.opendir(name)?;
71                this.write_scalar(result, dest)?;
72            }
73            "readdir$INODE64" => {
74                let [dirp] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
75                this.readdir(dirp, dest)?;
76            }
77            "realpath$DARWIN_EXTSN" => {
78                let [path, resolved_path] =
79                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
80                let result = this.realpath(path, resolved_path)?;
81                this.write_scalar(result, dest)?;
82            }
83
84            // Environment related shims
85            "_NSGetEnviron" => {
86                // FIXME: This does not have a direct test (#3179).
87                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
88                let environ = this.machine.env_vars.unix().environ();
89                this.write_pointer(environ, dest)?;
90            }
91
92            // Random data generation
93            "CCRandomGenerateBytes" => {
94                let [bytes, count] =
95                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
96                let bytes = this.read_pointer(bytes)?;
97                let count = this.read_target_usize(count)?;
98                let success = this.eval_libc_i32("kCCSuccess");
99                this.gen_random(bytes, count)?;
100                this.write_int(success, dest)?;
101            }
102
103            // Time related shims
104            "mach_absolute_time" => {
105                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
106                let result = this.mach_absolute_time()?;
107                this.write_scalar(result, dest)?;
108            }
109
110            "mach_timebase_info" => {
111                // FIXME: This does not have a direct test (#3179).
112                let [info] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
113                let result = this.mach_timebase_info(info)?;
114                this.write_scalar(result, dest)?;
115            }
116
117            "mach_wait_until" => {
118                // FIXME: This does not have a direct test (#3179).
119                let [deadline] =
120                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
121                let result = this.mach_wait_until(deadline)?;
122                this.write_scalar(result, dest)?;
123            }
124
125            // Access to command-line arguments
126            "_NSGetArgc" => {
127                // FIXME: This does not have a direct test (#3179).
128                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
129                this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
130            }
131            "_NSGetArgv" => {
132                // FIXME: This does not have a direct test (#3179).
133                let [] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
134                this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
135            }
136            "_NSGetExecutablePath" => {
137                // FIXME: This does not have a direct test (#3179).
138                let [buf, bufsize] =
139                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
140                this.check_no_isolation("`_NSGetExecutablePath`")?;
141
142                let buf_ptr = this.read_pointer(buf)?;
143                let bufsize = this.deref_pointer_as(bufsize, this.machine.layouts.u32)?;
144
145                // Using the host current_exe is a bit off, but consistent with Linux
146                // (where stdlib reads /proc/self/exe).
147                let path = std::env::current_exe().unwrap();
148                let (written, size_needed) = this.write_path_to_c_str(
149                    &path,
150                    buf_ptr,
151                    this.read_scalar(&bufsize)?.to_u32()?.into(),
152                )?;
153
154                if written {
155                    this.write_null(dest)?;
156                } else {
157                    this.write_scalar(Scalar::from_u32(size_needed.try_into().unwrap()), &bufsize)?;
158                    this.write_int(-1, dest)?;
159                }
160            }
161
162            // Thread-local storage
163            "_tlv_atexit" => {
164                let [dtor, data] =
165                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
166                let dtor = this.read_pointer(dtor)?;
167                let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
168                let data = this.read_scalar(data)?;
169                let active_thread = this.active_thread();
170                this.machine.tls.add_macos_thread_dtor(
171                    active_thread,
172                    dtor,
173                    data,
174                    this.machine.current_user_relevant_span(),
175                )?;
176            }
177
178            // Querying system information
179            "pthread_get_stackaddr_np" => {
180                // FIXME: This does not have a direct test (#3179).
181                let [thread] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
182                this.read_target_usize(thread)?;
183                let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
184                this.write_scalar(stack_addr, dest)?;
185            }
186            "pthread_get_stacksize_np" => {
187                // FIXME: This does not have a direct test (#3179).
188                let [thread] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
189                this.read_target_usize(thread)?;
190                let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
191                this.write_scalar(stack_size, dest)?;
192            }
193
194            // Threading
195            "pthread_setname_np" => {
196                let [name] = this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
197
198                // The real implementation has logic in two places:
199                // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
200                // * in kernel at https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/proc_info.c#L3218-L3227.
201                //
202                // The function in libc calls the kernel to validate
203                // the security policies and the input. If all of the requirements
204                // are met, then the name is set and 0 is returned. Otherwise, if
205                // the specified name is lomnger than MAXTHREADNAMESIZE, then
206                // ENAMETOOLONG is returned.
207                let thread = this.pthread_self()?;
208                let res = match this.pthread_setname_np(
209                    thread,
210                    this.read_scalar(name)?,
211                    this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?,
212                    /* truncate */ false,
213                )? {
214                    ThreadNameResult::Ok => Scalar::from_u32(0),
215                    ThreadNameResult::NameTooLong => this.eval_libc("ENAMETOOLONG"),
216                    ThreadNameResult::ThreadNotFound => unreachable!(),
217                };
218                // Contrary to the manpage, `pthread_setname_np` on macOS still
219                // returns an integer indicating success.
220                this.write_scalar(res, dest)?;
221            }
222            "pthread_getname_np" => {
223                let [thread, name, len] =
224                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
225
226                // The function's behavior isn't portable between platforms.
227                // In case of macOS, a truncated name (due to a too small buffer)
228                // does not lead to an error.
229                //
230                // For details, see the implementation at
231                // https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175.
232                // The key part is the strlcpy, which truncates the resulting value,
233                // but always null terminates (except for zero sized buffers).
234                let res = match this.pthread_getname_np(
235                    this.read_scalar(thread)?,
236                    this.read_scalar(name)?,
237                    this.read_scalar(len)?,
238                    /* truncate */ true,
239                )? {
240                    ThreadNameResult::Ok => Scalar::from_u32(0),
241                    // `NameTooLong` is possible when the buffer is zero sized,
242                    ThreadNameResult::NameTooLong => Scalar::from_u32(0),
243                    ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
244                };
245                this.write_scalar(res, dest)?;
246            }
247            "pthread_threadid_np" => {
248                let [thread, tid_ptr] =
249                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
250                let res = this.apple_pthread_threadid_np(thread, tid_ptr)?;
251                this.write_scalar(res, dest)?;
252            }
253
254            // Synchronization primitives
255            "os_sync_wait_on_address" => {
256                let [addr_op, value_op, size_op, flags_op] =
257                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
258                this.os_sync_wait_on_address(
259                    addr_op,
260                    value_op,
261                    size_op,
262                    flags_op,
263                    MacOsFutexTimeout::None,
264                    dest,
265                )?;
266            }
267            "os_sync_wait_on_address_with_deadline" => {
268                let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
269                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
270                this.os_sync_wait_on_address(
271                    addr_op,
272                    value_op,
273                    size_op,
274                    flags_op,
275                    MacOsFutexTimeout::Absolute { clock_op, timeout_op },
276                    dest,
277                )?;
278            }
279            "os_sync_wait_on_address_with_timeout" => {
280                let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
281                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
282                this.os_sync_wait_on_address(
283                    addr_op,
284                    value_op,
285                    size_op,
286                    flags_op,
287                    MacOsFutexTimeout::Relative { clock_op, timeout_op },
288                    dest,
289                )?;
290            }
291            "os_sync_wake_by_address_any" => {
292                let [addr_op, size_op, flags_op] =
293                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
294                this.os_sync_wake_by_address(
295                    addr_op, size_op, flags_op, /* all */ false, dest,
296                )?;
297            }
298            "os_sync_wake_by_address_all" => {
299                let [addr_op, size_op, flags_op] =
300                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
301                this.os_sync_wake_by_address(
302                    addr_op, size_op, flags_op, /* all */ true, dest,
303                )?;
304            }
305            "os_unfair_lock_lock" => {
306                let [lock_op] =
307                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
308                this.os_unfair_lock_lock(lock_op)?;
309            }
310            "os_unfair_lock_trylock" => {
311                let [lock_op] =
312                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
313                this.os_unfair_lock_trylock(lock_op, dest)?;
314            }
315            "os_unfair_lock_unlock" => {
316                let [lock_op] =
317                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
318                this.os_unfair_lock_unlock(lock_op)?;
319            }
320            "os_unfair_lock_assert_owner" => {
321                let [lock_op] =
322                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
323                this.os_unfair_lock_assert_owner(lock_op)?;
324            }
325            "os_unfair_lock_assert_not_owner" => {
326                let [lock_op] =
327                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
328                this.os_unfair_lock_assert_not_owner(lock_op)?;
329            }
330
331            "pthread_cond_timedwait_relative_np" => {
332                let [cond, mutex, reltime] =
333                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
334                this.pthread_cond_timedwait(
335                    cond, mutex, reltime, dest, /* macos_relative_np */ true,
336                )?;
337            }
338
339            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
340            // These shims are enabled only when the caller is in the standard library.
341            "confstr" => {
342                let [_key, _buf, _buflen] =
343                    this.check_shim_sig_deprecated(abi, CanonAbi::C, link_name, args)?;
344                // We just pretend that no configuration key exists, and return EINVAL.
345                this.set_last_error(LibcError("EINVAL"))?;
346                this.write_null(dest)?;
347            }
348
349            _ => return interp_ok(EmulateItemResult::NotSupported),
350        };
351
352        interp_ok(EmulateItemResult::NeedsReturn)
353    }
354}