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