1 use log::trace;
2 
3 use rustc_middle::mir;
4 use rustc_middle::ty::layout::LayoutOf;
5 use rustc_span::Symbol;
6 use rustc_target::abi::{Align, Size};
7 use rustc_target::spec::abi::Abi;
8 
9 use crate::*;
10 use shims::foreign_items::EmulateByNameResult;
11 use shims::posix::fs::EvalContextExt as _;
12 use shims::posix::sync::EvalContextExt as _;
13 use shims::posix::thread::EvalContextExt as _;
14 
15 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
16 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
emulate_foreign_item_by_name( &mut self, link_name: Symbol, abi: Abi, args: &[OpTy<'tcx, Tag>], dest: &PlaceTy<'tcx, Tag>, ret: mir::BasicBlock, ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>>17     fn emulate_foreign_item_by_name(
18         &mut self,
19         link_name: Symbol,
20         abi: Abi,
21         args: &[OpTy<'tcx, Tag>],
22         dest: &PlaceTy<'tcx, Tag>,
23         ret: mir::BasicBlock,
24     ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
25         let this = self.eval_context_mut();
26 
27         match &*link_name.as_str() {
28             // Environment related shims
29             "getenv" => {
30                 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
31                 let result = this.getenv(name)?;
32                 this.write_pointer(result, dest)?;
33             }
34             "unsetenv" => {
35                 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
36                 let result = this.unsetenv(name)?;
37                 this.write_scalar(Scalar::from_i32(result), dest)?;
38             }
39             "setenv" => {
40                 let &[ref name, ref value, ref overwrite] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
41                 this.read_scalar(overwrite)?.to_i32()?;
42                 let result = this.setenv(name, value)?;
43                 this.write_scalar(Scalar::from_i32(result), dest)?;
44             }
45             "getcwd" => {
46                 let &[ref buf, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
47                 let result = this.getcwd(buf, size)?;
48                 this.write_pointer(result, dest)?;
49             }
50             "chdir" => {
51                 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
52                 let result = this.chdir(path)?;
53                 this.write_scalar(Scalar::from_i32(result), dest)?;
54             }
55 
56             // File related shims
57             "open" | "open64" => {
58                 let &[ref path, ref flag, ref mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
59                 let result = this.open(path, flag, mode)?;
60                 this.write_scalar(Scalar::from_i32(result), dest)?;
61             }
62             "fcntl" => {
63                 // `fcntl` is variadic. The argument count is checked based on the first argument
64                 // in `this.fcntl()`, so we do not use `check_shim` here.
65                 this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
66                 let result = this.fcntl(args)?;
67                 this.write_scalar(Scalar::from_i32(result), dest)?;
68             }
69             "read" => {
70                 let &[ref fd, ref buf, ref count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
71                 let fd = this.read_scalar(fd)?.to_i32()?;
72                 let buf = this.read_pointer(buf)?;
73                 let count = this.read_scalar(count)?.to_machine_usize(this)?;
74                 let result = this.read(fd, buf, count)?;
75                 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
76             }
77             "write" => {
78                 let &[ref fd, ref buf, ref n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
79                 let fd = this.read_scalar(fd)?.to_i32()?;
80                 let buf = this.read_pointer(buf)?;
81                 let count = this.read_scalar(n)?.to_machine_usize(this)?;
82                 trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
83                 let result = this.write(fd, buf, count)?;
84                 // Now, `result` is the value we return back to the program.
85                 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
86             }
87             "unlink" => {
88                 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
89                 let result = this.unlink(path)?;
90                 this.write_scalar(Scalar::from_i32(result), dest)?;
91             }
92             "symlink" => {
93                 let &[ref target, ref linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
94                 let result = this.symlink(target, linkpath)?;
95                 this.write_scalar(Scalar::from_i32(result), dest)?;
96             }
97             "rename" => {
98                 let &[ref oldpath, ref newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
99                 let result = this.rename(oldpath, newpath)?;
100                 this.write_scalar(Scalar::from_i32(result), dest)?;
101             }
102             "mkdir" => {
103                 let &[ref path, ref mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
104                 let result = this.mkdir(path, mode)?;
105                 this.write_scalar(Scalar::from_i32(result), dest)?;
106             }
107             "rmdir" => {
108                 let &[ref path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
109                 let result = this.rmdir(path)?;
110                 this.write_scalar(Scalar::from_i32(result), dest)?;
111             }
112             "closedir" => {
113                 let &[ref dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
114                 let result = this.closedir(dirp)?;
115                 this.write_scalar(Scalar::from_i32(result), dest)?;
116             }
117             "lseek" | "lseek64" => {
118                 let &[ref fd, ref offset, ref whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
119                 let result = this.lseek64(fd, offset, whence)?;
120                 // "lseek" is only used on macOS which is 64bit-only, so `i64` always works.
121                 this.write_scalar(Scalar::from_i64(result), dest)?;
122             }
123             "fsync" => {
124                 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
125                 let result = this.fsync(fd)?;
126                 this.write_scalar(Scalar::from_i32(result), dest)?;
127             }
128             "fdatasync" => {
129                 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
130                 let result = this.fdatasync(fd)?;
131                 this.write_scalar(Scalar::from_i32(result), dest)?;
132             }
133             "readlink" => {
134                 let &[ref pathname, ref buf, ref bufsize] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
135                 let result = this.readlink(pathname, buf, bufsize)?;
136                 this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
137             }
138 
139             // Allocation
140             "posix_memalign" => {
141                 let &[ref ret, ref align, ref size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
142                 let ret = this.deref_operand(ret)?;
143                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
144                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
145                 // Align must be power of 2, and also at least ptr-sized (POSIX rules).
146                 if !align.is_power_of_two() {
147                     throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align);
148                 }
149                 if align < this.pointer_size().bytes() {
150                     throw_ub_format!(
151                         "posix_memalign: alignment must be at least the size of a pointer, but is {}",
152                         align,
153                     );
154                 }
155 
156                 if size == 0 {
157                     this.write_null(&ret.into())?;
158                 } else {
159                     let ptr = this.memory.allocate(
160                         Size::from_bytes(size),
161                         Align::from_bytes(align).unwrap(),
162                         MiriMemoryKind::C.into(),
163                     )?;
164                     this.write_pointer(ptr, &ret.into())?;
165                 }
166                 this.write_null(dest)?;
167             }
168 
169             // Dynamic symbol loading
170             "dlsym" => {
171                 let &[ref handle, ref symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
172                 this.read_scalar(handle)?.to_machine_usize(this)?;
173                 let symbol = this.read_pointer(symbol)?;
174                 let symbol_name = this.read_c_str(symbol)?;
175                 if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? {
176                     let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
177                     this.write_pointer(ptr, dest)?;
178                 } else {
179                     this.write_null(dest)?;
180                 }
181             }
182 
183             // Querying system information
184             "sysconf" => {
185                 let &[ref name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
186                 let name = this.read_scalar(name)?.to_i32()?;
187 
188                 let sysconfs = &[
189                     ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())),
190                     ("_SC_NPROCESSORS_CONF", Scalar::from_int(NUM_CPUS, this.pointer_size())),
191                     ("_SC_NPROCESSORS_ONLN", Scalar::from_int(NUM_CPUS, this.pointer_size())),
192                 ];
193                 let mut result = None;
194                 for &(sysconf_name, value) in sysconfs {
195                     let sysconf_name = this.eval_libc_i32(sysconf_name)?;
196                     if sysconf_name == name {
197                         result = Some(value);
198                         break;
199                     }
200                 }
201                 if let Some(result) = result {
202                     this.write_scalar(result, dest)?;
203                 } else {
204                     throw_unsup_format!("unimplemented sysconf name: {}", name)
205                 }
206             }
207 
208             // Thread-local storage
209             "pthread_key_create" => {
210                 let &[ref key, ref dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
211                 let key_place = this.deref_operand(key)?;
212                 let dtor = this.read_pointer(dtor)?;
213 
214                 // Extract the function type out of the signature (that seems easier than constructing it ourselves).
215                 let dtor = if !this.ptr_is_null(dtor)? {
216                     Some(this.memory.get_fn(dtor)?.as_instance()?)
217                 } else {
218                     None
219                 };
220 
221                 // Figure out how large a pthread TLS key actually is.
222                 // To this end, deref the argument type. This is `libc::pthread_key_t`.
223                 let key_type = key.layout.ty
224                     .builtin_deref(true)
225                     .ok_or_else(|| err_ub_format!(
226                         "wrong signature used for `pthread_key_create`: first argument must be a raw pointer."
227                     ))?
228                     .ty;
229                 let key_layout = this.layout_of(key_type)?;
230 
231                 // Create key and write it into the memory where `key_ptr` wants it.
232                 let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?;
233                 this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place.into())?;
234 
235                 // Return success (`0`).
236                 this.write_null(dest)?;
237             }
238             "pthread_key_delete" => {
239                 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
240                 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?;
241                 this.machine.tls.delete_tls_key(key)?;
242                 // Return success (0)
243                 this.write_null(dest)?;
244             }
245             "pthread_getspecific" => {
246                 let &[ref key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
247                 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?;
248                 let active_thread = this.get_active_thread();
249                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
250                 this.write_scalar(ptr, dest)?;
251             }
252             "pthread_setspecific" => {
253                 let &[ref key, ref new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
254                 let key = this.read_scalar(key)?.check_init()?.to_bits(key.layout.size)?;
255                 let active_thread = this.get_active_thread();
256                 let new_data = this.read_scalar(new_ptr)?;
257                 this.machine.tls.store_tls(key, active_thread, new_data.check_init()?, &*this.tcx)?;
258 
259                 // Return success (`0`).
260                 this.write_null(dest)?;
261             }
262 
263             // Synchronization primitives
264             "pthread_mutexattr_init" => {
265                 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
266                 let result = this.pthread_mutexattr_init(attr)?;
267                 this.write_scalar(Scalar::from_i32(result), dest)?;
268             }
269             "pthread_mutexattr_settype" => {
270                 let &[ref attr, ref kind] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
271                 let result = this.pthread_mutexattr_settype(attr, kind)?;
272                 this.write_scalar(Scalar::from_i32(result), dest)?;
273             }
274             "pthread_mutexattr_destroy" => {
275                 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
276                 let result = this.pthread_mutexattr_destroy(attr)?;
277                 this.write_scalar(Scalar::from_i32(result), dest)?;
278             }
279             "pthread_mutex_init" => {
280                 let &[ref mutex, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
281                 let result = this.pthread_mutex_init(mutex, attr)?;
282                 this.write_scalar(Scalar::from_i32(result), dest)?;
283             }
284             "pthread_mutex_lock" => {
285                 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
286                 let result = this.pthread_mutex_lock(mutex)?;
287                 this.write_scalar(Scalar::from_i32(result), dest)?;
288             }
289             "pthread_mutex_trylock" => {
290                 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
291                 let result = this.pthread_mutex_trylock(mutex)?;
292                 this.write_scalar(Scalar::from_i32(result), dest)?;
293             }
294             "pthread_mutex_unlock" => {
295                 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
296                 let result = this.pthread_mutex_unlock(mutex)?;
297                 this.write_scalar(Scalar::from_i32(result), dest)?;
298             }
299             "pthread_mutex_destroy" => {
300                 let &[ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
301                 let result = this.pthread_mutex_destroy(mutex)?;
302                 this.write_scalar(Scalar::from_i32(result), dest)?;
303             }
304             "pthread_rwlock_rdlock" => {
305                 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
306                 let result = this.pthread_rwlock_rdlock(rwlock)?;
307                 this.write_scalar(Scalar::from_i32(result), dest)?;
308             }
309             "pthread_rwlock_tryrdlock" => {
310                 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
311                 let result = this.pthread_rwlock_tryrdlock(rwlock)?;
312                 this.write_scalar(Scalar::from_i32(result), dest)?;
313             }
314             "pthread_rwlock_wrlock" => {
315                 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
316                 let result = this.pthread_rwlock_wrlock(rwlock)?;
317                 this.write_scalar(Scalar::from_i32(result), dest)?;
318             }
319             "pthread_rwlock_trywrlock" => {
320                 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
321                 let result = this.pthread_rwlock_trywrlock(rwlock)?;
322                 this.write_scalar(Scalar::from_i32(result), dest)?;
323             }
324             "pthread_rwlock_unlock" => {
325                 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
326                 let result = this.pthread_rwlock_unlock(rwlock)?;
327                 this.write_scalar(Scalar::from_i32(result), dest)?;
328             }
329             "pthread_rwlock_destroy" => {
330                 let &[ref rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
331                 let result = this.pthread_rwlock_destroy(rwlock)?;
332                 this.write_scalar(Scalar::from_i32(result), dest)?;
333             }
334             "pthread_condattr_init" => {
335                 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
336                 let result = this.pthread_condattr_init(attr)?;
337                 this.write_scalar(Scalar::from_i32(result), dest)?;
338             }
339             "pthread_condattr_destroy" => {
340                 let &[ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
341                 let result = this.pthread_condattr_destroy(attr)?;
342                 this.write_scalar(Scalar::from_i32(result), dest)?;
343             }
344             "pthread_cond_init" => {
345                 let &[ref cond, ref attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
346                 let result = this.pthread_cond_init(cond, attr)?;
347                 this.write_scalar(Scalar::from_i32(result), dest)?;
348             }
349             "pthread_cond_signal" => {
350                 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
351                 let result = this.pthread_cond_signal(cond)?;
352                 this.write_scalar(Scalar::from_i32(result), dest)?;
353             }
354             "pthread_cond_broadcast" => {
355                 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
356                 let result = this.pthread_cond_broadcast(cond)?;
357                 this.write_scalar(Scalar::from_i32(result), dest)?;
358             }
359             "pthread_cond_wait" => {
360                 let &[ref cond, ref mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
361                 let result = this.pthread_cond_wait(cond, mutex)?;
362                 this.write_scalar(Scalar::from_i32(result), dest)?;
363             }
364             "pthread_cond_timedwait" => {
365                 let &[ref cond, ref mutex, ref abstime] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
366                 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
367             }
368             "pthread_cond_destroy" => {
369                 let &[ref cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
370                 let result = this.pthread_cond_destroy(cond)?;
371                 this.write_scalar(Scalar::from_i32(result), dest)?;
372             }
373 
374             // Threading
375             "pthread_create" => {
376                 let &[ref thread, ref attr, ref start, ref arg] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
377                 let result = this.pthread_create(thread, attr, start, arg)?;
378                 this.write_scalar(Scalar::from_i32(result), dest)?;
379             }
380             "pthread_join" => {
381                 let &[ref thread, ref retval] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
382                 let result = this.pthread_join(thread, retval)?;
383                 this.write_scalar(Scalar::from_i32(result), dest)?;
384             }
385             "pthread_detach" => {
386                 let &[ref thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
387                 let result = this.pthread_detach(thread)?;
388                 this.write_scalar(Scalar::from_i32(result), dest)?;
389             }
390             "pthread_self" => {
391                 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
392                 this.pthread_self(dest)?;
393             }
394             "sched_yield" => {
395                 let &[] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
396                 let result = this.sched_yield()?;
397                 this.write_scalar(Scalar::from_i32(result), dest)?;
398             }
399             "nanosleep" => {
400                 let &[ref req, ref rem] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
401                 let result = this.nanosleep(req, rem)?;
402                 this.write_scalar(Scalar::from_i32(result), dest)?;
403             }
404 
405             // Miscellaneous
406             "isatty" => {
407                 let &[ref fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
408                 this.read_scalar(fd)?.to_i32()?;
409                 // "returns 1 if fd is an open file descriptor referring to a terminal; otherwise 0 is returned, and errno is set to indicate the error"
410                 // FIXME: we just say nothing is a terminal.
411                 let enotty = this.eval_libc("ENOTTY")?;
412                 this.set_last_error(enotty)?;
413                 this.write_null(dest)?;
414             }
415             "pthread_atfork" => {
416                 let &[ref prepare, ref parent, ref child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
417                 this.read_pointer(prepare)?;
418                 this.read_pointer(parent)?;
419                 this.read_pointer(child)?;
420                 // We do not support forking, so there is nothing to do here.
421                 this.write_null(dest)?;
422             }
423 
424             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
425             // These shims are enabled only when the caller is in the standard library.
426             "pthread_attr_getguardsize"
427             if this.frame_in_std() => {
428                 let &[ref _attr, ref guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
429                 let guard_size = this.deref_operand(guard_size)?;
430                 let guard_size_layout = this.libc_ty_layout("size_t")?;
431                 this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), &guard_size.into())?;
432 
433                 // Return success (`0`).
434                 this.write_null(dest)?;
435             }
436 
437             | "pthread_attr_init"
438             | "pthread_attr_destroy"
439             if this.frame_in_std() => {
440                 let &[_] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
441                 this.write_null(dest)?;
442             }
443             | "pthread_attr_setstacksize"
444             if this.frame_in_std() => {
445                 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
446                 this.write_null(dest)?;
447             }
448 
449             | "signal"
450             | "sigaltstack"
451             if this.frame_in_std() => {
452                 let &[_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
453                 this.write_null(dest)?;
454             }
455             | "sigaction"
456             | "mprotect"
457             if this.frame_in_std() => {
458                 let &[_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
459                 this.write_null(dest)?;
460             }
461 
462             // Platform-specific shims
463             _ => {
464                 match this.tcx.sess.target.os.as_str() {
465                     "linux" => return shims::posix::linux::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
466                     "macos" => return shims::posix::macos::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
467                     _ => unreachable!(),
468                 }
469             }
470         };
471 
472         Ok(EmulateByNameResult::NeedsJumping)
473     }
474 }
475