Skip to main content

miri/shims/unix/linux/
foreign_items.rs

1use rustc_abi::CanonAbi;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5
6use self::shims::unix::linux::mem::EvalContextExt as _;
7use self::shims::unix::linux_like::eventfd::EvalContextExt as _;
8use self::shims::unix::linux_like::syscall::syscall;
9use crate::machine::{SIGRTMAX, SIGRTMIN};
10use crate::shims::unix::foreign_items::EvalContextExt as _;
11use crate::shims::unix::*;
12use crate::*;
13
14// The documentation of glibc complains that the kernel never exposes
15// TASK_COMM_LEN through the headers, so it's assumed to always be 16 bytes
16// long including a null terminator.
17const TASK_COMM_LEN: u64 = 16;
18
19pub fn is_dyn_sym(name: &str) -> bool {
20    matches!(name, "gettid" | "statx")
21}
22
23impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
24pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
25    fn emulate_foreign_item_inner(
26        &mut self,
27        link_name: Symbol,
28        abi: &FnAbi<'tcx, Ty<'tcx>>,
29        args: &[OpTy<'tcx>],
30        dest: &MPlaceTy<'tcx>,
31    ) -> InterpResult<'tcx, EmulateItemResult> {
32        let this = self.eval_context_mut();
33
34        // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
35
36        match link_name.as_str() {
37            // File related shims
38            "open64" => {
39                // `open64` is variadic, the third argument is only present when the second argument
40                // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
41                let ([path_raw, flag], varargs) =
42                    this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
43                let result = this.open(path_raw, flag, varargs)?;
44                this.write_scalar(result, dest)?;
45            }
46            "pread64" => {
47                // FIXME: This does not have a direct test (#3179).
48                let [fd, buf, count, offset] = this.check_shim_sig(
49                    shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
50                    link_name,
51                    abi,
52                    args,
53                )?;
54                let fd = this.read_scalar(fd)?.to_i32()?;
55                let buf = this.read_pointer(buf)?;
56                let count = this.read_target_usize(count)?;
57                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
58                this.read(fd, buf, count, Some(offset), dest)?;
59            }
60            "pwrite64" => {
61                // FIXME: This does not have a direct test (#3179).
62                let [fd, buf, n, offset] = this.check_shim_sig(
63                    shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
64                    link_name,
65                    abi,
66                    args,
67                )?;
68                let fd = this.read_scalar(fd)?.to_i32()?;
69                let buf = this.read_pointer(buf)?;
70                let count = this.read_target_usize(n)?;
71                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
72                trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
73                this.write(fd, buf, count, Some(offset), dest)?;
74            }
75            "lseek64" => {
76                // FIXME: This does not have a direct test (#3179).
77                let [fd, offset, whence] = this.check_shim_sig(
78                    shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
79                    link_name,
80                    abi,
81                    args,
82                )?;
83                let fd = this.read_scalar(fd)?.to_i32()?;
84                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
85                let whence = this.read_scalar(whence)?.to_i32()?;
86                this.lseek(fd, offset, whence, dest)?;
87            }
88            "ftruncate64" => {
89                let [fd, length] = this.check_shim_sig(
90                    shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32),
91                    link_name,
92                    abi,
93                    args,
94                )?;
95                let fd = this.read_scalar(fd)?.to_i32()?;
96                let length = this.read_scalar(length)?.to_int(length.layout.size)?;
97                let result = this.ftruncate64(fd, length)?;
98                this.write_scalar(result, dest)?;
99            }
100            "posix_fallocate64" => {
101                let [fd, offset, len] = this.check_shim_sig(
102                    shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32),
103                    link_name,
104                    abi,
105                    args,
106                )?;
107
108                let fd = this.read_scalar(fd)?.to_i32()?;
109                let offset = this.read_scalar(offset)?.to_i64()?;
110                let len = this.read_scalar(len)?.to_i64()?;
111
112                let result = this.posix_fallocate(fd, offset, len)?;
113                this.write_scalar(result, dest)?;
114            }
115            "readdir64" => {
116                let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
117                this.readdir(dirp, dest)?;
118            }
119            "sync_file_range" => {
120                let [fd, offset, nbytes, flags] =
121                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
122                let result = this.sync_file_range(fd, offset, nbytes, flags)?;
123                this.write_scalar(result, dest)?;
124            }
125            "statx" => {
126                let [dirfd, pathname, flags, mask, statxbuf] =
127                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
128                let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
129                this.write_scalar(result, dest)?;
130            }
131            // epoll, eventfd
132            "epoll_create1" => {
133                let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
134                let result = this.epoll_create1(flag)?;
135                this.write_scalar(result, dest)?;
136            }
137            "epoll_ctl" => {
138                let [epfd, op, fd, event] =
139                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
140                let result = this.epoll_ctl(epfd, op, fd, event)?;
141                this.write_scalar(result, dest)?;
142            }
143            "epoll_wait" => {
144                let [epfd, events, maxevents, timeout] =
145                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
146                this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
147            }
148            "eventfd" => {
149                let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
150                let result = this.eventfd(val, flag)?;
151                this.write_scalar(result, dest)?;
152            }
153
154            // Threading
155            "pthread_setname_np" => {
156                let [thread, name] =
157                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
158                let res = match this.pthread_setname_np(
159                    this.read_scalar(thread)?,
160                    this.read_scalar(name)?,
161                    TASK_COMM_LEN,
162                    /* truncate */ false,
163                )? {
164                    ThreadNameResult::Ok => Scalar::from_u32(0),
165                    ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
166                    // Act like we failed to open `/proc/self/task/$tid/comm`.
167                    ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
168                };
169                this.write_scalar(res, dest)?;
170            }
171            "pthread_getname_np" => {
172                let [thread, name, len] =
173                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
174                // The function's behavior isn't portable between platforms.
175                // In case of glibc, the length of the output buffer must
176                // be not shorter than TASK_COMM_LEN.
177                let len = this.read_scalar(len)?;
178                let res = if len.to_target_usize(this)? >= TASK_COMM_LEN {
179                    match this.pthread_getname_np(
180                        this.read_scalar(thread)?,
181                        this.read_scalar(name)?,
182                        len,
183                        /* truncate*/ false,
184                    )? {
185                        ThreadNameResult::Ok => Scalar::from_u32(0),
186                        ThreadNameResult::NameTooLong => unreachable!(),
187                        // Act like we failed to open `/proc/self/task/$tid/comm`.
188                        ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
189                    }
190                } else {
191                    this.eval_libc("ERANGE")
192                };
193                this.write_scalar(res, dest)?;
194            }
195            "gettid" => {
196                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
197                let result = this.unix_gettid(link_name.as_str())?;
198                this.write_scalar(result, dest)?;
199            }
200
201            // Dynamically invoked syscalls
202            "syscall" => {
203                syscall(this, link_name, abi, args, dest)?;
204            }
205
206            // Miscellaneous
207            "mmap64" => {
208                let [addr, length, prot, flags, fd, offset] =
209                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
210                let offset = this.read_scalar(offset)?.to_i64()?;
211                let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?;
212                this.write_scalar(ptr, dest)?;
213            }
214            "mremap" => {
215                let ([old_address, old_size, new_size, flags], _) =
216                    this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
217                let ptr = this.mremap(old_address, old_size, new_size, flags)?;
218                this.write_scalar(ptr, dest)?;
219            }
220            "__xpg_strerror_r" => {
221                let [errnum, buf, buflen] =
222                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
223                let result = this.strerror_r(errnum, buf, buflen)?;
224                this.write_scalar(result, dest)?;
225            }
226            "__errno_location" => {
227                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
228                let errno_place = this.last_error_place()?;
229                this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
230            }
231            "__libc_current_sigrtmin" => {
232                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
233
234                this.write_int(SIGRTMIN, dest)?;
235            }
236            "__libc_current_sigrtmax" => {
237                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
238
239                this.write_int(SIGRTMAX, dest)?;
240            }
241
242            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
243            // These shims are enabled only when the caller is in the standard library.
244            "pthread_getattr_np" if this.frame_in_std() => {
245                let [_thread, _attr] =
246                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
247                this.write_null(dest)?;
248            }
249            "gnu_get_libc_version"
250                if this.frame_in_std()
251                    && this.tcx.sess.target.env == rustc_target::spec::Env::Gnu =>
252            {
253                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
254                // We have to be at least version 2.26 so that std does not call `res_init`.
255                // This returns a C string, so we have to add a null terminator.
256                let version = "2.26\0";
257                let version = this.allocate_str_dedup(version)?;
258                this.write_pointer(version.ptr(), dest)?;
259            }
260
261            _ => return interp_ok(EmulateItemResult::NotSupported),
262        };
263
264        interp_ok(EmulateItemResult::NeedsReturn)
265    }
266}