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