miri/shims/unix/
foreign_items.rs

1use std::ffi::OsStr;
2use std::str;
3
4use rustc_abi::{ExternAbi, Size};
5use rustc_middle::ty::Ty;
6use rustc_middle::ty::layout::LayoutOf;
7use rustc_span::Symbol;
8use rustc_target::callconv::{Conv, FnAbi};
9
10use self::shims::unix::android::foreign_items as android;
11use self::shims::unix::freebsd::foreign_items as freebsd;
12use self::shims::unix::linux::foreign_items as linux;
13use self::shims::unix::macos::foreign_items as macos;
14use self::shims::unix::solarish::foreign_items as solarish;
15use crate::concurrency::cpu_affinity::CpuAffinityMask;
16use crate::shims::alloc::EvalContextExt as _;
17use crate::shims::unix::*;
18use crate::*;
19
20pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
21    match name {
22        // Used for tests.
23        "isatty" => true,
24        // `signal` is set up as a weak symbol in `init_extern_statics` (on Android) so we might as
25        // well allow it in `dlsym`.
26        "signal" => true,
27        // needed at least on macOS to avoid file-based fallback in getrandom
28        "getentropy" | "getrandom" => true,
29        // Give specific OSes a chance to allow their symbols.
30        _ =>
31            match target_os {
32                "android" => android::is_dyn_sym(name),
33                "freebsd" => freebsd::is_dyn_sym(name),
34                "linux" => linux::is_dyn_sym(name),
35                "macos" => macos::is_dyn_sym(name),
36                "solaris" | "illumos" => solarish::is_dyn_sym(name),
37                _ => false,
38            },
39    }
40}
41
42impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
43pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
44    // Querying system information
45    fn sysconf(&mut self, val: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
46        let this = self.eval_context_mut();
47
48        let name = this.read_scalar(val)?.to_i32()?;
49        // FIXME: Which of these are POSIX, and which are GNU/Linux?
50        // At least the names seem to all also exist on macOS.
51        let sysconfs: &[(&str, fn(&MiriInterpCx<'_>) -> Scalar)] = &[
52            ("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
53            ("_SC_PAGE_SIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
54            ("_SC_NPROCESSORS_CONF", |this| {
55                Scalar::from_int(this.machine.num_cpus, this.pointer_size())
56            }),
57            ("_SC_NPROCESSORS_ONLN", |this| {
58                Scalar::from_int(this.machine.num_cpus, this.pointer_size())
59            }),
60            // 512 seems to be a reasonable default. The value is not critical, in
61            // the sense that getpwuid_r takes and checks the buffer length.
62            ("_SC_GETPW_R_SIZE_MAX", |this| Scalar::from_int(512, this.pointer_size())),
63            // Miri doesn't have a fixed limit on FDs, but we may be limited in terms of how
64            // many *host* FDs we can open. Just use some arbitrary, pretty big value;
65            // this can be adjusted if it causes problems.
66            // The spec imposes a minimum of `_POSIX_OPEN_MAX` (20).
67            ("_SC_OPEN_MAX", |this| Scalar::from_int(2_i32.pow(16), this.pointer_size())),
68        ];
69        for &(sysconf_name, value) in sysconfs {
70            let sysconf_name = this.eval_libc_i32(sysconf_name);
71            if sysconf_name == name {
72                return interp_ok(value(this));
73            }
74        }
75        throw_unsup_format!("unimplemented sysconf name: {}", name)
76    }
77
78    fn strerror_r(
79        &mut self,
80        errnum: &OpTy<'tcx>,
81        buf: &OpTy<'tcx>,
82        buflen: &OpTy<'tcx>,
83    ) -> InterpResult<'tcx, Scalar> {
84        let this = self.eval_context_mut();
85
86        let errnum = this.read_scalar(errnum)?;
87        let buf = this.read_pointer(buf)?;
88        let buflen = this.read_target_usize(buflen)?;
89        let error = this.try_errnum_to_io_error(errnum)?;
90        let formatted = match error {
91            Some(err) => format!("{err}"),
92            None => format!("<unknown errnum in strerror_r: {errnum}>"),
93        };
94        let (complete, _) = this.write_os_str_to_c_str(OsStr::new(&formatted), buf, buflen)?;
95        if complete {
96            interp_ok(Scalar::from_i32(0))
97        } else {
98            interp_ok(Scalar::from_i32(this.eval_libc_i32("ERANGE")))
99        }
100    }
101
102    fn emulate_foreign_item_inner(
103        &mut self,
104        link_name: Symbol,
105        abi: &FnAbi<'tcx, Ty<'tcx>>,
106        args: &[OpTy<'tcx>],
107        dest: &MPlaceTy<'tcx>,
108    ) -> InterpResult<'tcx, EmulateItemResult> {
109        let this = self.eval_context_mut();
110
111        // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
112        match link_name.as_str() {
113            // Environment related shims
114            "getenv" => {
115                let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
116                let result = this.getenv(name)?;
117                this.write_pointer(result, dest)?;
118            }
119            "unsetenv" => {
120                let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
121                let result = this.unsetenv(name)?;
122                this.write_scalar(result, dest)?;
123            }
124            "setenv" => {
125                let [name, value, overwrite] = this.check_shim(abi, Conv::C, link_name, args)?;
126                this.read_scalar(overwrite)?.to_i32()?;
127                let result = this.setenv(name, value)?;
128                this.write_scalar(result, dest)?;
129            }
130            "getcwd" => {
131                let [buf, size] = this.check_shim(abi, Conv::C, link_name, args)?;
132                let result = this.getcwd(buf, size)?;
133                this.write_pointer(result, dest)?;
134            }
135            "chdir" => {
136                let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
137                let result = this.chdir(path)?;
138                this.write_scalar(result, dest)?;
139            }
140            "getpid" => {
141                let [] = this.check_shim(abi, Conv::C, link_name, args)?;
142                let result = this.getpid()?;
143                this.write_scalar(result, dest)?;
144            }
145            "sysconf" => {
146                let [val] = this.check_shim(abi, Conv::C, link_name, args)?;
147                let result = this.sysconf(val)?;
148                this.write_scalar(result, dest)?;
149            }
150            // File descriptors
151            "read" => {
152                let [fd, buf, count] = this.check_shim(abi, Conv::C, link_name, args)?;
153                let fd = this.read_scalar(fd)?.to_i32()?;
154                let buf = this.read_pointer(buf)?;
155                let count = this.read_target_usize(count)?;
156                this.read(fd, buf, count, None, dest)?;
157            }
158            "write" => {
159                let [fd, buf, n] = this.check_shim(abi, Conv::C, link_name, args)?;
160                let fd = this.read_scalar(fd)?.to_i32()?;
161                let buf = this.read_pointer(buf)?;
162                let count = this.read_target_usize(n)?;
163                trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
164                this.write(fd, buf, count, None, dest)?;
165            }
166            "pread" => {
167                let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
168                let fd = this.read_scalar(fd)?.to_i32()?;
169                let buf = this.read_pointer(buf)?;
170                let count = this.read_target_usize(count)?;
171                let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
172                this.read(fd, buf, count, Some(offset), dest)?;
173            }
174            "pwrite" => {
175                let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
176                let fd = this.read_scalar(fd)?.to_i32()?;
177                let buf = this.read_pointer(buf)?;
178                let count = this.read_target_usize(n)?;
179                let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
180                trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
181                this.write(fd, buf, count, Some(offset), dest)?;
182            }
183            "pread64" => {
184                let [fd, buf, count, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
185                let fd = this.read_scalar(fd)?.to_i32()?;
186                let buf = this.read_pointer(buf)?;
187                let count = this.read_target_usize(count)?;
188                let offset =
189                    this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
190                this.read(fd, buf, count, Some(offset), dest)?;
191            }
192            "pwrite64" => {
193                let [fd, buf, n, offset] = this.check_shim(abi, Conv::C, link_name, args)?;
194                let fd = this.read_scalar(fd)?.to_i32()?;
195                let buf = this.read_pointer(buf)?;
196                let count = this.read_target_usize(n)?;
197                let offset =
198                    this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
199                trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
200                this.write(fd, buf, count, Some(offset), dest)?;
201            }
202            "close" => {
203                let [fd] = this.check_shim_abi(
204                    link_name,
205                    abi,
206                    ExternAbi::C { unwind: false },
207                    [this.tcx.types.i32],
208                    this.tcx.types.i32,
209                    args,
210                )?;
211                let result = this.close(fd)?;
212                this.write_scalar(result, dest)?;
213            }
214            "fcntl" => {
215                let ([fd_num, cmd], varargs) =
216                    this.check_shim_variadic(abi, Conv::C, link_name, args)?;
217                let result = this.fcntl(fd_num, cmd, varargs)?;
218                this.write_scalar(result, dest)?;
219            }
220            "dup" => {
221                let [old_fd] = this.check_shim(abi, Conv::C, link_name, args)?;
222                let old_fd = this.read_scalar(old_fd)?.to_i32()?;
223                let new_fd = this.dup(old_fd)?;
224                this.write_scalar(new_fd, dest)?;
225            }
226            "dup2" => {
227                let [old_fd, new_fd] = this.check_shim(abi, Conv::C, link_name, args)?;
228                let old_fd = this.read_scalar(old_fd)?.to_i32()?;
229                let new_fd = this.read_scalar(new_fd)?.to_i32()?;
230                let result = this.dup2(old_fd, new_fd)?;
231                this.write_scalar(result, dest)?;
232            }
233            "flock" => {
234                let [fd, op] = this.check_shim(abi, Conv::C, link_name, args)?;
235                let fd = this.read_scalar(fd)?.to_i32()?;
236                let op = this.read_scalar(op)?.to_i32()?;
237                let result = this.flock(fd, op)?;
238                this.write_scalar(result, dest)?;
239            }
240
241            // File and file system access
242            "open" | "open64" => {
243                // `open` is variadic, the third argument is only present when the second argument
244                // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
245                let ([path_raw, flag], varargs) =
246                    this.check_shim_variadic(abi, Conv::C, link_name, args)?;
247                let result = this.open(path_raw, flag, varargs)?;
248                this.write_scalar(result, dest)?;
249            }
250            "unlink" => {
251                let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
252                let result = this.unlink(path)?;
253                this.write_scalar(result, dest)?;
254            }
255            "symlink" => {
256                let [target, linkpath] = this.check_shim(abi, Conv::C, link_name, args)?;
257                let result = this.symlink(target, linkpath)?;
258                this.write_scalar(result, dest)?;
259            }
260            "rename" => {
261                let [oldpath, newpath] = this.check_shim(abi, Conv::C, link_name, args)?;
262                let result = this.rename(oldpath, newpath)?;
263                this.write_scalar(result, dest)?;
264            }
265            "mkdir" => {
266                let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?;
267                let result = this.mkdir(path, mode)?;
268                this.write_scalar(result, dest)?;
269            }
270            "rmdir" => {
271                let [path] = this.check_shim(abi, Conv::C, link_name, args)?;
272                let result = this.rmdir(path)?;
273                this.write_scalar(result, dest)?;
274            }
275            "opendir" => {
276                let [name] = this.check_shim(abi, Conv::C, link_name, args)?;
277                let result = this.opendir(name)?;
278                this.write_scalar(result, dest)?;
279            }
280            "closedir" => {
281                let [dirp] = this.check_shim(abi, Conv::C, link_name, args)?;
282                let result = this.closedir(dirp)?;
283                this.write_scalar(result, dest)?;
284            }
285            "lseek64" => {
286                let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?;
287                let fd = this.read_scalar(fd)?.to_i32()?;
288                let offset = this.read_scalar(offset)?.to_i64()?;
289                let whence = this.read_scalar(whence)?.to_i32()?;
290                let result = this.lseek64(fd, offset.into(), whence)?;
291                this.write_scalar(result, dest)?;
292            }
293            "lseek" => {
294                let [fd, offset, whence] = this.check_shim(abi, Conv::C, link_name, args)?;
295                let fd = this.read_scalar(fd)?.to_i32()?;
296                let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
297                let whence = this.read_scalar(whence)?.to_i32()?;
298                let result = this.lseek64(fd, offset, whence)?;
299                this.write_scalar(result, dest)?;
300            }
301            "ftruncate64" => {
302                let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?;
303                let fd = this.read_scalar(fd)?.to_i32()?;
304                let length = this.read_scalar(length)?.to_i64()?;
305                let result = this.ftruncate64(fd, length.into())?;
306                this.write_scalar(result, dest)?;
307            }
308            "ftruncate" => {
309                let [fd, length] = this.check_shim(abi, Conv::C, link_name, args)?;
310                let fd = this.read_scalar(fd)?.to_i32()?;
311                let length = this.read_scalar(length)?.to_int(this.libc_ty_layout("off_t").size)?;
312                let result = this.ftruncate64(fd, length)?;
313                this.write_scalar(result, dest)?;
314            }
315            "fsync" => {
316                let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
317                let result = this.fsync(fd)?;
318                this.write_scalar(result, dest)?;
319            }
320            "fdatasync" => {
321                let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
322                let result = this.fdatasync(fd)?;
323                this.write_scalar(result, dest)?;
324            }
325            "readlink" => {
326                let [pathname, buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
327                let result = this.readlink(pathname, buf, bufsize)?;
328                this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
329            }
330            "posix_fadvise" => {
331                let [fd, offset, len, advice] = this.check_shim(abi, Conv::C, link_name, args)?;
332                this.read_scalar(fd)?.to_i32()?;
333                this.read_target_isize(offset)?;
334                this.read_target_isize(len)?;
335                this.read_scalar(advice)?.to_i32()?;
336                // fadvise is only informational, we can ignore it.
337                this.write_null(dest)?;
338            }
339            "realpath" => {
340                let [path, resolved_path] = this.check_shim(abi, Conv::C, link_name, args)?;
341                let result = this.realpath(path, resolved_path)?;
342                this.write_scalar(result, dest)?;
343            }
344            "mkstemp" => {
345                let [template] = this.check_shim(abi, Conv::C, link_name, args)?;
346                let result = this.mkstemp(template)?;
347                this.write_scalar(result, dest)?;
348            }
349
350            // Unnamed sockets and pipes
351            "socketpair" => {
352                let [domain, type_, protocol, sv] =
353                    this.check_shim(abi, Conv::C, link_name, args)?;
354                let result = this.socketpair(domain, type_, protocol, sv)?;
355                this.write_scalar(result, dest)?;
356            }
357            "pipe" => {
358                let [pipefd] = this.check_shim(abi, Conv::C, link_name, args)?;
359                let result = this.pipe2(pipefd, /*flags*/ None)?;
360                this.write_scalar(result, dest)?;
361            }
362            "pipe2" => {
363                // Currently this function does not exist on all Unixes, e.g. on macOS.
364                this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?;
365                let [pipefd, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
366                let result = this.pipe2(pipefd, Some(flags))?;
367                this.write_scalar(result, dest)?;
368            }
369
370            // Time
371            "gettimeofday" => {
372                let [tv, tz] = this.check_shim(abi, Conv::C, link_name, args)?;
373                let result = this.gettimeofday(tv, tz)?;
374                this.write_scalar(result, dest)?;
375            }
376            "localtime_r" => {
377                let [timep, result_op] = this.check_shim(abi, Conv::C, link_name, args)?;
378                let result = this.localtime_r(timep, result_op)?;
379                this.write_pointer(result, dest)?;
380            }
381            "clock_gettime" => {
382                let [clk_id, tp] = this.check_shim(abi, Conv::C, link_name, args)?;
383                let result = this.clock_gettime(clk_id, tp)?;
384                this.write_scalar(result, dest)?;
385            }
386
387            // Allocation
388            "posix_memalign" => {
389                let [memptr, align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
390                let result = this.posix_memalign(memptr, align, size)?;
391                this.write_scalar(result, dest)?;
392            }
393
394            "mmap" => {
395                let [addr, length, prot, flags, fd, offset] =
396                    this.check_shim(abi, Conv::C, link_name, args)?;
397                let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
398                let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
399                this.write_scalar(ptr, dest)?;
400            }
401            "munmap" => {
402                let [addr, length] = this.check_shim(abi, Conv::C, link_name, args)?;
403                let result = this.munmap(addr, length)?;
404                this.write_scalar(result, dest)?;
405            }
406
407            "reallocarray" => {
408                // Currently this function does not exist on all Unixes, e.g. on macOS.
409                this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
410                let [ptr, nmemb, size] = this.check_shim(abi, Conv::C, link_name, args)?;
411                let ptr = this.read_pointer(ptr)?;
412                let nmemb = this.read_target_usize(nmemb)?;
413                let size = this.read_target_usize(size)?;
414                // reallocarray checks a possible overflow and returns ENOMEM
415                // if that happens.
416                //
417                // Linux: https://www.unix.com/man-page/linux/3/reallocarray/
418                // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=reallocarray
419                match this.compute_size_in_bytes(Size::from_bytes(size), nmemb) {
420                    None => {
421                        this.set_last_error(LibcError("ENOMEM"))?;
422                        this.write_null(dest)?;
423                    }
424                    Some(len) => {
425                        let res = this.realloc(ptr, len.bytes())?;
426                        this.write_pointer(res, dest)?;
427                    }
428                }
429            }
430            "aligned_alloc" => {
431                // This is a C11 function, we assume all Unixes have it.
432                // (MSVC explicitly does not support this.)
433                let [align, size] = this.check_shim(abi, Conv::C, link_name, args)?;
434                let res = this.aligned_alloc(align, size)?;
435                this.write_pointer(res, dest)?;
436            }
437
438            // Dynamic symbol loading
439            "dlsym" => {
440                let [handle, symbol] = this.check_shim(abi, Conv::C, link_name, args)?;
441                this.read_target_usize(handle)?;
442                let symbol = this.read_pointer(symbol)?;
443                let name = this.read_c_str(symbol)?;
444                if let Ok(name) = str::from_utf8(name)
445                    && is_dyn_sym(name, &this.tcx.sess.target.os)
446                {
447                    let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
448                    this.write_pointer(ptr, dest)?;
449                } else {
450                    this.write_null(dest)?;
451                }
452            }
453
454            // Thread-local storage
455            "pthread_key_create" => {
456                let [key, dtor] = this.check_shim(abi, Conv::C, link_name, args)?;
457                let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?;
458                let dtor = this.read_pointer(dtor)?;
459
460                // Extract the function type out of the signature (that seems easier than constructing it ourselves).
461                let dtor = if !this.ptr_is_null(dtor)? {
462                    Some(this.get_ptr_fn(dtor)?.as_instance()?)
463                } else {
464                    None
465                };
466
467                // Figure out how large a pthread TLS key actually is.
468                // To this end, deref the argument type. This is `libc::pthread_key_t`.
469                let key_type = key.layout.ty
470                    .builtin_deref(true)
471                    .ok_or_else(|| err_ub_format!(
472                        "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
473                    ))?;
474                let key_layout = this.layout_of(key_type)?;
475
476                // Create key and write it into the memory where `key_ptr` wants it.
477                let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
478                this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place)?;
479
480                // Return success (`0`).
481                this.write_null(dest)?;
482            }
483            "pthread_key_delete" => {
484                let [key] = this.check_shim(abi, Conv::C, link_name, args)?;
485                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
486                this.machine.tls.delete_tls_key(key)?;
487                // Return success (0)
488                this.write_null(dest)?;
489            }
490            "pthread_getspecific" => {
491                let [key] = this.check_shim(abi, Conv::C, link_name, args)?;
492                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
493                let active_thread = this.active_thread();
494                let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
495                this.write_scalar(ptr, dest)?;
496            }
497            "pthread_setspecific" => {
498                let [key, new_ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
499                let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
500                let active_thread = this.active_thread();
501                let new_data = this.read_scalar(new_ptr)?;
502                this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
503
504                // Return success (`0`).
505                this.write_null(dest)?;
506            }
507
508            // Synchronization primitives
509            "pthread_mutexattr_init" => {
510                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
511                this.pthread_mutexattr_init(attr)?;
512                this.write_null(dest)?;
513            }
514            "pthread_mutexattr_settype" => {
515                let [attr, kind] = this.check_shim(abi, Conv::C, link_name, args)?;
516                let result = this.pthread_mutexattr_settype(attr, kind)?;
517                this.write_scalar(result, dest)?;
518            }
519            "pthread_mutexattr_destroy" => {
520                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
521                this.pthread_mutexattr_destroy(attr)?;
522                this.write_null(dest)?;
523            }
524            "pthread_mutex_init" => {
525                let [mutex, attr] = this.check_shim(abi, Conv::C, link_name, args)?;
526                this.pthread_mutex_init(mutex, attr)?;
527                this.write_null(dest)?;
528            }
529            "pthread_mutex_lock" => {
530                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
531                this.pthread_mutex_lock(mutex, dest)?;
532            }
533            "pthread_mutex_trylock" => {
534                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
535                let result = this.pthread_mutex_trylock(mutex)?;
536                this.write_scalar(result, dest)?;
537            }
538            "pthread_mutex_unlock" => {
539                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
540                let result = this.pthread_mutex_unlock(mutex)?;
541                this.write_scalar(result, dest)?;
542            }
543            "pthread_mutex_destroy" => {
544                let [mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
545                this.pthread_mutex_destroy(mutex)?;
546                this.write_int(0, dest)?;
547            }
548            "pthread_rwlock_rdlock" => {
549                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
550                this.pthread_rwlock_rdlock(rwlock, dest)?;
551            }
552            "pthread_rwlock_tryrdlock" => {
553                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
554                let result = this.pthread_rwlock_tryrdlock(rwlock)?;
555                this.write_scalar(result, dest)?;
556            }
557            "pthread_rwlock_wrlock" => {
558                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
559                this.pthread_rwlock_wrlock(rwlock, dest)?;
560            }
561            "pthread_rwlock_trywrlock" => {
562                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
563                let result = this.pthread_rwlock_trywrlock(rwlock)?;
564                this.write_scalar(result, dest)?;
565            }
566            "pthread_rwlock_unlock" => {
567                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
568                this.pthread_rwlock_unlock(rwlock)?;
569                this.write_null(dest)?;
570            }
571            "pthread_rwlock_destroy" => {
572                let [rwlock] = this.check_shim(abi, Conv::C, link_name, args)?;
573                this.pthread_rwlock_destroy(rwlock)?;
574                this.write_null(dest)?;
575            }
576            "pthread_condattr_init" => {
577                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
578                this.pthread_condattr_init(attr)?;
579                this.write_null(dest)?;
580            }
581            "pthread_condattr_setclock" => {
582                let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?;
583                let result = this.pthread_condattr_setclock(attr, clock_id)?;
584                this.write_scalar(result, dest)?;
585            }
586            "pthread_condattr_getclock" => {
587                let [attr, clock_id] = this.check_shim(abi, Conv::C, link_name, args)?;
588                this.pthread_condattr_getclock(attr, clock_id)?;
589                this.write_null(dest)?;
590            }
591            "pthread_condattr_destroy" => {
592                let [attr] = this.check_shim(abi, Conv::C, link_name, args)?;
593                this.pthread_condattr_destroy(attr)?;
594                this.write_null(dest)?;
595            }
596            "pthread_cond_init" => {
597                let [cond, attr] = this.check_shim(abi, Conv::C, link_name, args)?;
598                this.pthread_cond_init(cond, attr)?;
599                this.write_null(dest)?;
600            }
601            "pthread_cond_signal" => {
602                let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
603                this.pthread_cond_signal(cond)?;
604                this.write_null(dest)?;
605            }
606            "pthread_cond_broadcast" => {
607                let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
608                this.pthread_cond_broadcast(cond)?;
609                this.write_null(dest)?;
610            }
611            "pthread_cond_wait" => {
612                let [cond, mutex] = this.check_shim(abi, Conv::C, link_name, args)?;
613                this.pthread_cond_wait(cond, mutex, dest)?;
614            }
615            "pthread_cond_timedwait" => {
616                let [cond, mutex, abstime] = this.check_shim(abi, Conv::C, link_name, args)?;
617                this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
618            }
619            "pthread_cond_destroy" => {
620                let [cond] = this.check_shim(abi, Conv::C, link_name, args)?;
621                this.pthread_cond_destroy(cond)?;
622                this.write_null(dest)?;
623            }
624
625            // Threading
626            "pthread_create" => {
627                let [thread, attr, start, arg] = this.check_shim(abi, Conv::C, link_name, args)?;
628                this.pthread_create(thread, attr, start, arg)?;
629                this.write_null(dest)?;
630            }
631            "pthread_join" => {
632                let [thread, retval] = this.check_shim(abi, Conv::C, link_name, args)?;
633                let res = this.pthread_join(thread, retval)?;
634                this.write_scalar(res, dest)?;
635            }
636            "pthread_detach" => {
637                let [thread] = this.check_shim(abi, Conv::C, link_name, args)?;
638                let res = this.pthread_detach(thread)?;
639                this.write_scalar(res, dest)?;
640            }
641            "pthread_self" => {
642                let [] = this.check_shim(abi, Conv::C, link_name, args)?;
643                let res = this.pthread_self()?;
644                this.write_scalar(res, dest)?;
645            }
646            "sched_yield" => {
647                let [] = this.check_shim(abi, Conv::C, link_name, args)?;
648                this.sched_yield()?;
649                this.write_null(dest)?;
650            }
651            "nanosleep" => {
652                let [req, rem] = this.check_shim(abi, Conv::C, link_name, args)?;
653                let result = this.nanosleep(req, rem)?;
654                this.write_scalar(result, dest)?;
655            }
656            "sched_getaffinity" => {
657                // Currently this function does not exist on all Unixes, e.g. on macOS.
658                this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
659                let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
660                let pid = this.read_scalar(pid)?.to_u32()?;
661                let cpusetsize = this.read_target_usize(cpusetsize)?;
662                let mask = this.read_pointer(mask)?;
663
664                // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
665                let thread_id = match pid {
666                    0 => this.active_thread(),
667                    _ =>
668                        throw_unsup_format!(
669                            "`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"
670                        ),
671                };
672
673                // The mask is stored in chunks, and the size must be a whole number of chunks.
674                let chunk_size = CpuAffinityMask::chunk_size(this);
675
676                if this.ptr_is_null(mask)? {
677                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
678                } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
679                    // we only copy whole chunks of size_of::<c_ulong>()
680                    this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
681                } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
682                    let cpuset = cpuset.clone();
683                    // we only copy whole chunks of size_of::<c_ulong>()
684                    let byte_count =
685                        Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap());
686                    this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?;
687                    this.write_null(dest)?;
688                } else {
689                    // The thread whose ID is pid could not be found
690                    this.set_last_error_and_return(LibcError("ESRCH"), dest)?;
691                }
692            }
693            "sched_setaffinity" => {
694                // Currently this function does not exist on all Unixes, e.g. on macOS.
695                this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
696                let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
697                let pid = this.read_scalar(pid)?.to_u32()?;
698                let cpusetsize = this.read_target_usize(cpusetsize)?;
699                let mask = this.read_pointer(mask)?;
700
701                // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
702                let thread_id = match pid {
703                    0 => this.active_thread(),
704                    _ =>
705                        throw_unsup_format!(
706                            "`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"
707                        ),
708                };
709
710                if this.ptr_is_null(mask)? {
711                    this.set_last_error_and_return(LibcError("EFAULT"), dest)?;
712                } else {
713                    // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
714                    // Any unspecified bytes are treated as zero here (none of the CPUs are configured).
715                    // This is not exactly documented, so we assume that this is the behavior in practice.
716                    let bits_slice =
717                        this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?;
718                    // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES`
719                    let bits_array: [u8; CpuAffinityMask::CPU_MASK_BYTES] =
720                        std::array::from_fn(|i| bits_slice.get(i).copied().unwrap_or(0));
721                    match CpuAffinityMask::from_array(this, this.machine.num_cpus, bits_array) {
722                        Some(cpuset) => {
723                            this.machine.thread_cpu_affinity.insert(thread_id, cpuset);
724                            this.write_null(dest)?;
725                        }
726                        None => {
727                            // The intersection between the mask and the available CPUs was empty.
728                            this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
729                        }
730                    }
731                }
732            }
733
734            // Miscellaneous
735            "isatty" => {
736                let [fd] = this.check_shim(abi, Conv::C, link_name, args)?;
737                let result = this.isatty(fd)?;
738                this.write_scalar(result, dest)?;
739            }
740            "pthread_atfork" => {
741                let [prepare, parent, child] = this.check_shim(abi, Conv::C, link_name, args)?;
742                this.read_pointer(prepare)?;
743                this.read_pointer(parent)?;
744                this.read_pointer(child)?;
745                // We do not support forking, so there is nothing to do here.
746                this.write_null(dest)?;
747            }
748            "getentropy" => {
749                // This function is non-standard but exists with the same signature and behavior on
750                // Linux, macOS, FreeBSD and Solaris/Illumos.
751                this.check_target_os(
752                    &["linux", "macos", "freebsd", "illumos", "solaris", "android"],
753                    link_name,
754                )?;
755                let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
756                let buf = this.read_pointer(buf)?;
757                let bufsize = this.read_target_usize(bufsize)?;
758
759                // getentropy sets errno to EIO when the buffer size exceeds 256 bytes.
760                // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html
761                // Linux: https://man7.org/linux/man-pages/man3/getentropy.3.html
762                // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html
763                // Solaris/Illumos: https://illumos.org/man/3C/getentropy
764                if bufsize > 256 {
765                    this.set_last_error_and_return(LibcError("EIO"), dest)?;
766                } else {
767                    this.gen_random(buf, bufsize)?;
768                    this.write_null(dest)?;
769                }
770            }
771
772            "strerror_r" => {
773                let [errnum, buf, buflen] = this.check_shim(abi, Conv::C, link_name, args)?;
774                let result = this.strerror_r(errnum, buf, buflen)?;
775                this.write_scalar(result, dest)?;
776            }
777
778            "getrandom" => {
779                // This function is non-standard but exists with the same signature and behavior on
780                // Linux, FreeBSD and Solaris/Illumos.
781                this.check_target_os(
782                    &["linux", "freebsd", "illumos", "solaris", "android"],
783                    link_name,
784                )?;
785                let [ptr, len, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
786                let ptr = this.read_pointer(ptr)?;
787                let len = this.read_target_usize(len)?;
788                let _flags = this.read_scalar(flags)?.to_i32()?;
789                // We ignore the flags, just always use the same PRNG / host RNG.
790                this.gen_random(ptr, len)?;
791                this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
792            }
793            "arc4random_buf" => {
794                // This function is non-standard but exists with the same signature and
795                // same behavior (eg never fails) on FreeBSD and Solaris/Illumos.
796                this.check_target_os(&["freebsd", "illumos", "solaris"], link_name)?;
797                let [ptr, len] = this.check_shim(abi, Conv::C, link_name, args)?;
798                let ptr = this.read_pointer(ptr)?;
799                let len = this.read_target_usize(len)?;
800                this.gen_random(ptr, len)?;
801            }
802            "_Unwind_RaiseException" => {
803                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
804                // It was originally specified as part of the Itanium C++ ABI:
805                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
806                // On Linux it is
807                // documented as part of the LSB:
808                // https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib--unwind-raiseexception.html
809                // Basically every other UNIX uses the exact same api though. Arm also references
810                // back to the Itanium C++ ABI for the definition of `_Unwind_RaiseException` for
811                // arm64:
812                // https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#toc-entry-35
813                // For arm32 they did something custom, but similar enough that the same
814                // `_Unwind_RaiseException` impl in miri should work:
815                // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
816                this.check_target_os(
817                    &["linux", "freebsd", "illumos", "solaris", "android", "macos"],
818                    link_name,
819                )?;
820                // This function looks and behaves excatly like miri_start_unwind.
821                let [payload] = this.check_shim(abi, Conv::C, link_name, args)?;
822                this.handle_miri_start_unwind(payload)?;
823                return interp_ok(EmulateItemResult::NeedsUnwind);
824            }
825            "getuid" | "geteuid" => {
826                let [] = this.check_shim(abi, Conv::C, link_name, args)?;
827                // For now, just pretend we always have this fixed UID.
828                this.write_int(UID, dest)?;
829            }
830
831            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
832            // These shims are enabled only when the caller is in the standard library.
833            "pthread_attr_getguardsize" if this.frame_in_std() => {
834                let [_attr, guard_size] = this.check_shim(abi, Conv::C, link_name, args)?;
835                let guard_size_layout = this.libc_ty_layout("size_t");
836                let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?;
837                this.write_scalar(
838                    Scalar::from_uint(this.machine.page_size, guard_size_layout.size),
839                    &guard_size,
840                )?;
841
842                // Return success (`0`).
843                this.write_null(dest)?;
844            }
845
846            "pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => {
847                let [_] = this.check_shim(abi, Conv::C, link_name, args)?;
848                this.write_null(dest)?;
849            }
850            "pthread_attr_setstacksize" if this.frame_in_std() => {
851                let [_, _] = this.check_shim(abi, Conv::C, link_name, args)?;
852                this.write_null(dest)?;
853            }
854
855            "pthread_attr_getstack" if this.frame_in_std() => {
856                // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
857                // Hence we can mostly ignore the input `attr_place`.
858                let [attr_place, addr_place, size_place] =
859                    this.check_shim(abi, Conv::C, link_name, args)?;
860                let _attr_place =
861                    this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?;
862                let addr_place = this.deref_pointer_as(addr_place, this.machine.layouts.usize)?;
863                let size_place = this.deref_pointer_as(size_place, this.machine.layouts.usize)?;
864
865                this.write_scalar(
866                    Scalar::from_uint(this.machine.stack_addr, this.pointer_size()),
867                    &addr_place,
868                )?;
869                this.write_scalar(
870                    Scalar::from_uint(this.machine.stack_size, this.pointer_size()),
871                    &size_place,
872                )?;
873
874                // Return success (`0`).
875                this.write_null(dest)?;
876            }
877
878            "signal" | "sigaltstack" if this.frame_in_std() => {
879                let [_, _] = this.check_shim(abi, Conv::C, link_name, args)?;
880                this.write_null(dest)?;
881            }
882            "sigaction" | "mprotect" if this.frame_in_std() => {
883                let [_, _, _] = this.check_shim(abi, Conv::C, link_name, args)?;
884                this.write_null(dest)?;
885            }
886
887            "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => {
888                // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish
889                let [uid, pwd, buf, buflen, result] =
890                    this.check_shim(abi, Conv::C, link_name, args)?;
891                this.check_no_isolation("`getpwuid_r`")?;
892
893                let uid = this.read_scalar(uid)?.to_u32()?;
894                let pwd = this.deref_pointer_as(pwd, this.libc_ty_layout("passwd"))?;
895                let buf = this.read_pointer(buf)?;
896                let buflen = this.read_target_usize(buflen)?;
897                let result = this.deref_pointer_as(result, this.machine.layouts.mut_raw_ptr)?;
898
899                // Must be for "us".
900                if uid != UID {
901                    throw_unsup_format!("`getpwuid_r` on other users is not supported");
902                }
903
904                // Reset all fields to `uninit` to make sure nobody reads them.
905                // (This is a std-only shim so we are okay with such hacks.)
906                this.write_uninit(&pwd)?;
907
908                // We only set the home_dir field.
909                #[allow(deprecated)]
910                let home_dir = std::env::home_dir().unwrap();
911                let (written, _) = this.write_path_to_c_str(&home_dir, buf, buflen)?;
912                let pw_dir = this.project_field_named(&pwd, "pw_dir")?;
913                this.write_pointer(buf, &pw_dir)?;
914
915                if written {
916                    this.write_pointer(pwd.ptr(), &result)?;
917                    this.write_null(dest)?;
918                } else {
919                    this.write_null(&result)?;
920                    this.write_scalar(this.eval_libc("ERANGE"), dest)?;
921                }
922            }
923
924            // Platform-specific shims
925            _ => {
926                let target_os = &*this.tcx.sess.target.os;
927                return match target_os {
928                    "android" =>
929                        android::EvalContextExt::emulate_foreign_item_inner(
930                            this, link_name, abi, args, dest,
931                        ),
932                    "freebsd" =>
933                        freebsd::EvalContextExt::emulate_foreign_item_inner(
934                            this, link_name, abi, args, dest,
935                        ),
936                    "linux" =>
937                        linux::EvalContextExt::emulate_foreign_item_inner(
938                            this, link_name, abi, args, dest,
939                        ),
940                    "macos" =>
941                        macos::EvalContextExt::emulate_foreign_item_inner(
942                            this, link_name, abi, args, dest,
943                        ),
944                    "solaris" | "illumos" =>
945                        solarish::EvalContextExt::emulate_foreign_item_inner(
946                            this, link_name, abi, args, dest,
947                        ),
948                    _ => interp_ok(EmulateItemResult::NotSupported),
949                };
950            }
951        };
952
953        interp_ok(EmulateItemResult::NeedsReturn)
954    }
955}