Skip to main content

miri/shims/unix/freebsd/
foreign_items.rs

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