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::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                // FIXME: This does not have a direct test (#3179).
49                let [fd, buf, count, offset] = this.check_shim_sig(
50                    shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
51                    link_name,
52                    abi,
53                    args,
54                )?;
55                let fd = this.read_scalar(fd)?.to_i32()?;
56                let buf = this.read_pointer(buf)?;
57                let count = this.read_target_usize(count)?;
58                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
59                this.read(fd, buf, count, Some(offset), dest)?;
60            }
61            "pwrite64" => {
62                // FIXME: This does not have a direct test (#3179).
63                let [fd, buf, n, offset] = this.check_shim_sig(
64                    shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
65                    link_name,
66                    abi,
67                    args,
68                )?;
69                let fd = this.read_scalar(fd)?.to_i32()?;
70                let buf = this.read_pointer(buf)?;
71                let count = this.read_target_usize(n)?;
72                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
73                trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
74                this.write(fd, buf, count, Some(offset), dest)?;
75            }
76            "lseek64" => {
77                // FIXME: This does not have a direct test (#3179).
78                let [fd, offset, whence] = this.check_shim_sig(
79                    shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
80                    link_name,
81                    abi,
82                    args,
83                )?;
84                let fd = this.read_scalar(fd)?.to_i32()?;
85                let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
86                let whence = this.read_scalar(whence)?.to_i32()?;
87                this.lseek(fd, offset, whence, dest)?;
88            }
89            "ftruncate64" => {
90                let [fd, length] = this.check_shim_sig(
91                    shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32),
92                    link_name,
93                    abi,
94                    args,
95                )?;
96                let fd = this.read_scalar(fd)?.to_i32()?;
97                let length = this.read_scalar(length)?.to_int(length.layout.size)?;
98                let result = this.ftruncate64(fd, length)?;
99                this.write_scalar(result, dest)?;
100            }
101            "posix_fallocate64" => {
102                let [fd, offset, len] = this.check_shim_sig(
103                    shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32),
104                    link_name,
105                    abi,
106                    args,
107                )?;
108
109                let fd = this.read_scalar(fd)?.to_i32()?;
110                let offset = this.read_scalar(offset)?.to_i64()?;
111                let len = this.read_scalar(len)?.to_i64()?;
112
113                let result = this.posix_fallocate(fd, offset, len)?;
114                this.write_scalar(result, dest)?;
115            }
116            "readdir64" => {
117                let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
118                this.readdir(dirp, dest)?;
119            }
120            "sync_file_range" => {
121                let [fd, offset, nbytes, flags] =
122                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
123                let result = this.sync_file_range(fd, offset, nbytes, flags)?;
124                this.write_scalar(result, dest)?;
125            }
126            "statx" => {
127                let [dirfd, pathname, flags, mask, statxbuf] =
128                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
129                let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
130                this.write_scalar(result, dest)?;
131            }
132            // epoll, eventfd
133            "epoll_create1" => {
134                let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
135                let result = this.epoll_create1(flag)?;
136                this.write_scalar(result, dest)?;
137            }
138            "epoll_ctl" => {
139                let [epfd, op, fd, event] =
140                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
141                let result = this.epoll_ctl(epfd, op, fd, event)?;
142                this.write_scalar(result, dest)?;
143            }
144            "epoll_wait" => {
145                let [epfd, events, maxevents, timeout] =
146                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
147                this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
148            }
149            "eventfd" => {
150                let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
151                let result = this.eventfd(val, flag)?;
152                this.write_scalar(result, dest)?;
153            }
154
155            // Threading
156            "pthread_setname_np" => {
157                let [thread, name] =
158                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
159                let res = match this.pthread_setname_np(
160                    this.read_scalar(thread)?,
161                    this.read_scalar(name)?,
162                    TASK_COMM_LEN,
163                    /* truncate */ false,
164                )? {
165                    ThreadNameResult::Ok => Scalar::from_u32(0),
166                    ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
167                    // Act like we failed to open `/proc/self/task/$tid/comm`.
168                    ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
169                };
170                this.write_scalar(res, dest)?;
171            }
172            "pthread_getname_np" => {
173                let [thread, name, len] =
174                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
175                // The function's behavior isn't portable between platforms.
176                // In case of glibc, the length of the output buffer must
177                // be not shorter than TASK_COMM_LEN.
178                let len = this.read_scalar(len)?;
179                let res = if len.to_target_usize(this)? >= TASK_COMM_LEN {
180                    match this.pthread_getname_np(
181                        this.read_scalar(thread)?,
182                        this.read_scalar(name)?,
183                        len,
184                        /* truncate*/ false,
185                    )? {
186                        ThreadNameResult::Ok => Scalar::from_u32(0),
187                        ThreadNameResult::NameTooLong => unreachable!(),
188                        // Act like we failed to open `/proc/self/task/$tid/comm`.
189                        ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
190                    }
191                } else {
192                    this.eval_libc("ERANGE")
193                };
194                this.write_scalar(res, dest)?;
195            }
196            "gettid" => {
197                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
198                let result = this.unix_gettid(link_name.as_str())?;
199                this.write_scalar(result, dest)?;
200            }
201
202            // Dynamically invoked syscalls
203            "syscall" => {
204                syscall(this, link_name, abi, args, dest)?;
205            }
206
207            // Miscellaneous
208            "mmap64" => {
209                let [addr, length, prot, flags, fd, offset] =
210                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
211                let offset = this.read_scalar(offset)?.to_i64()?;
212                let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?;
213                this.write_scalar(ptr, dest)?;
214            }
215            "mremap" => {
216                let ([old_address, old_size, new_size, flags], _) =
217                    this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
218                let ptr = this.mremap(old_address, old_size, new_size, flags)?;
219                this.write_scalar(ptr, dest)?;
220            }
221            "__xpg_strerror_r" => {
222                let [errnum, buf, buflen] =
223                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
224                let result = this.strerror_r(errnum, buf, buflen)?;
225                this.write_scalar(result, dest)?;
226            }
227            "__errno_location" => {
228                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
229                let errno_place = this.last_error_place()?;
230                this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
231            }
232            "__libc_current_sigrtmin" => {
233                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
234
235                this.write_int(SIGRTMIN, dest)?;
236            }
237            "__libc_current_sigrtmax" => {
238                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
239
240                this.write_int(SIGRTMAX, dest)?;
241            }
242
243            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
244            // These shims are enabled only when the caller is in the standard library.
245            "pthread_getattr_np" if this.frame_in_std() => {
246                let [_thread, _attr] =
247                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
248                this.write_null(dest)?;
249            }
250
251            _ => return interp_ok(EmulateItemResult::NotSupported),
252        };
253
254        interp_ok(EmulateItemResult::NeedsReturn)
255    }
256}