Skip to main content

miri/shims/unix/freebsd/
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 _;
7use crate::shims::unix::*;
8use crate::*;
9
10pub fn is_dyn_sym(_name: &str) -> bool {
11    false
12}
13
14impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
15pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
16    fn emulate_foreign_item_inner(
17        &mut self,
18        link_name: Symbol,
19        abi: &FnAbi<'tcx, Ty<'tcx>>,
20        args: &[OpTy<'tcx>],
21        dest: &MPlaceTy<'tcx>,
22    ) -> InterpResult<'tcx, EmulateItemResult> {
23        let this = self.eval_context_mut();
24        match link_name.as_str() {
25            // Threading
26            "pthread_setname_np" => {
27                let [thread, name] =
28                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
29                let max_len = u64::MAX; // FreeBSD does not seem to have a limit.
30                let res = match this.pthread_setname_np(
31                    this.read_scalar(thread)?,
32                    this.read_scalar(name)?,
33                    max_len,
34                    /* truncate */ false,
35                )? {
36                    ThreadNameResult::Ok => Scalar::from_u32(0),
37                    ThreadNameResult::NameTooLong => unreachable!(),
38                    ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
39                };
40                this.write_scalar(res, dest)?;
41            }
42            "pthread_getname_np" => {
43                let [thread, name, len] =
44                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
45                // FreeBSD's pthread_getname_np uses strlcpy, which truncates the resulting value,
46                // but always adds a null terminator (except for zero-sized buffers).
47                // https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144
48                let res = match this.pthread_getname_np(
49                    this.read_scalar(thread)?,
50                    this.read_scalar(name)?,
51                    this.read_scalar(len)?,
52                    /* truncate */ true,
53                )? {
54                    ThreadNameResult::Ok => Scalar::from_u32(0),
55                    // `NameTooLong` is possible when the buffer is zero sized,
56                    ThreadNameResult::NameTooLong => Scalar::from_u32(0),
57                    ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
58                };
59                this.write_scalar(res, dest)?;
60            }
61            "pthread_getthreadid_np" => {
62                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
63                let result = this.unix_gettid(link_name.as_str())?;
64                this.write_scalar(result, dest)?;
65            }
66
67            "cpuset_getaffinity" => {
68                // The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically.
69                let [level, which, id, set_size, mask] =
70                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
71
72                let level = this.read_scalar(level)?.to_i32()?;
73                let which = this.read_scalar(which)?.to_i32()?;
74                let id = this.read_scalar(id)?.to_i64()?;
75                let set_size = this.read_target_usize(set_size)?; // measured in bytes
76                let mask = this.read_pointer(mask)?;
77
78                if this.machine.thread_cpu_affinity.is_none() {
79                    throw_unsup_format!(
80                        "`cpuset_getaffinity` is not supported on #![no_core] programs"
81                    )
82                }
83
84                let _level_root = this.eval_libc_i32("CPU_LEVEL_ROOT");
85                let _level_cpuset = this.eval_libc_i32("CPU_LEVEL_CPUSET");
86                let level_which = this.eval_libc_i32("CPU_LEVEL_WHICH");
87
88                let _which_tid = this.eval_libc_i32("CPU_WHICH_TID");
89                let which_pid = this.eval_libc_i32("CPU_WHICH_PID");
90                let _which_jail = this.eval_libc_i32("CPU_WHICH_JAIL");
91                let _which_cpuset = this.eval_libc_i32("CPU_WHICH_CPUSET");
92                let _which_irq = this.eval_libc_i32("CPU_WHICH_IRQ");
93
94                // For sched_getaffinity, the current process is identified by -1.
95                // TODO: Use gettid? I'm (LorrensP-2158466) not that familiar with this api .
96                let id = match id {
97                    -1 => this.active_thread(),
98                    _ =>
99                        throw_unsup_format!(
100                            "`cpuset_getaffinity` is only supported with a pid of -1 (indicating the current thread)"
101                        ),
102                };
103
104                if this.ptr_is_null(mask)? {
105                    this.set_errno_and_return_neg1(LibcError("EFAULT"), dest)?;
106                }
107                // We only support CPU_LEVEL_WHICH and CPU_WHICH_PID for now.
108                // This is the bare minimum to make the tests pass.
109                else if level != level_which || which != which_pid {
110                    throw_unsup_format!(
111                        "`cpuset_getaffinity` is only supported with `level` set to CPU_LEVEL_WHICH and `which` set to CPU_WHICH_PID."
112                    );
113                } else if let Some(cpuset) =
114                    this.machine.thread_cpu_affinity.as_ref().unwrap().get(&id)
115                {
116                    // `cpusetsize` must be large enough to contain the entire CPU mask.
117                    // FreeBSD only uses `cpusetsize` to verify that it's sufficient for the kernel's CPU mask.
118                    // If it's too small, the syscall returns ERANGE.
119                    // If it's large enough, copying the kernel mask to user space is safe, regardless of the actual size.
120                    // See https://github.com/freebsd/freebsd-src/blob/909aa6781340f8c0b4ae01c6366bf1556ee2d1be/sys/kern/kern_cpuset.c#L1985
121                    if set_size < u64::from(this.machine.num_cpus).div_ceil(8) {
122                        this.set_errno_and_return_neg1(LibcError("ERANGE"), dest)?;
123                    } else {
124                        let cpuset = cpuset.clone();
125                        let byte_count =
126                            Ord::min(cpuset.as_slice().len(), set_size.try_into().unwrap());
127                        this.write_bytes_ptr(
128                            mask,
129                            cpuset.as_slice()[..byte_count].iter().copied(),
130                        )?;
131                        this.write_null(dest)?;
132                    }
133                } else {
134                    // `id` is always that of the active thread, so this is currently unreachable.
135                    unreachable!();
136                }
137            }
138
139            // Synchronization primitives
140            "_umtx_op" => {
141                let [obj, op, val, uaddr, uaddr2] =
142                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
143                this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?;
144            }
145
146            // File related shims
147            "stat@FBSD_1.0" => {
148                let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
149                let result = this.stat(path, buf)?;
150                this.write_scalar(result, dest)?;
151            }
152            "lstat@FBSD_1.0" => {
153                let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
154                let result = this.lstat(path, buf)?;
155                this.write_scalar(result, dest)?;
156            }
157            "fstat@FBSD_1.0" => {
158                let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
159                let result = this.fstat(fd, buf)?;
160                this.write_scalar(result, dest)?;
161            }
162            "readdir@FBSD_1.0" => {
163                let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
164                this.readdir(dirp, dest)?;
165            }
166            // Miscellaneous
167            "__error" => {
168                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
169                let errno_place = this.last_error_place()?;
170                this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
171            }
172            "__xuname" => {
173                // FreeBSD uses __xuname under the hood to implement uname, see:
174                // https://github.com/freebsd/freebsd-src/blob/3542d60fb8042474f66fbf2d779ed8c5a80d0f78/sys/sys/utsname.h#L64
175                // https://github.com/freebsd/freebsd-src/blob/3542d60fb8042474f66fbf2d779ed8c5a80d0f78/lib/libc/gen/uname.c#L44
176                let [size, uname] = this.check_shim_sig(
177                    shim_sig!(extern "C" fn(i32, *mut _) -> i32),
178                    link_name,
179                    abi,
180                    args,
181                )?;
182                let result = this.uname(uname, Some(size))?;
183                this.write_scalar(result, dest)?;
184            }
185
186            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
187            // These shims are enabled only when the caller is in the standard library.
188            "pthread_attr_get_np" if this.frame_in_std() => {
189                let [_thread, _attr] =
190                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
191                this.write_null(dest)?;
192            }
193
194            _ => return interp_ok(EmulateItemResult::NotSupported),
195        }
196        interp_ok(EmulateItemResult::NeedsReturn)
197    }
198}