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