]> git.lizzy.rs Git - rust.git/blob - src/shims/posix/linux/foreign_items.rs
Auto merge of #1568 - fusion-engineering-forks:futex, r=RalfJung
[rust.git] / src / shims / posix / linux / foreign_items.rs
1 use rustc_middle::mir;
2
3 use crate::*;
4 use crate::helpers::check_arg_count;
5 use shims::posix::fs::EvalContextExt as _;
6 use shims::posix::linux::sync::futex;
7 use shims::posix::sync::EvalContextExt as _;
8 use shims::posix::thread::EvalContextExt as _;
9
10 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
11 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
12     fn emulate_foreign_item_by_name(
13         &mut self,
14         link_name: &str,
15         args: &[OpTy<'tcx, Tag>],
16         dest: PlaceTy<'tcx, Tag>,
17         _ret: mir::BasicBlock,
18     ) -> InterpResult<'tcx, bool> {
19         let this = self.eval_context_mut();
20
21         match link_name {
22             // errno
23             "__errno_location" => {
24                 let &[] = check_arg_count(args)?;
25                 let errno_place = this.last_error_place()?;
26                 this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
27             }
28
29             // File related shims (but also see "syscall" below for statx)
30             // These symbols have different names on Linux and macOS, which is the only reason they are not
31             // in the `posix` module.
32             "close" => {
33                 let &[fd] = check_arg_count(args)?;
34                 let result = this.close(fd)?;
35                 this.write_scalar(Scalar::from_i32(result), dest)?;
36             }
37             "opendir" => {
38                 let &[name] = check_arg_count(args)?;
39                 let result = this.opendir(name)?;
40                 this.write_scalar(result, dest)?;
41             }
42             "readdir64_r" => {
43                 let &[dirp, entry, result] = check_arg_count(args)?;
44                 let result = this.linux_readdir64_r(dirp, entry, result)?;
45                 this.write_scalar(Scalar::from_i32(result), dest)?;
46             }
47             "ftruncate64" => {
48                 let &[fd, length] = check_arg_count(args)?;
49                 let result = this.ftruncate64(fd, length)?;
50                 this.write_scalar(Scalar::from_i32(result), dest)?;
51             }
52             // Linux-only
53             "posix_fadvise" => {
54                 let &[fd, offset, len, advice] = check_arg_count(args)?;
55                 this.read_scalar(fd)?.to_i32()?;
56                 this.read_scalar(offset)?.to_machine_isize(this)?;
57                 this.read_scalar(len)?.to_machine_isize(this)?;
58                 this.read_scalar(advice)?.to_i32()?;
59                 // fadvise is only informational, we can ignore it.
60                 this.write_null(dest)?;
61             }
62             "sync_file_range" => {
63                 let &[fd, offset, nbytes, flags] = check_arg_count(args)?;
64                 let result = this.sync_file_range(fd, offset, nbytes, flags)?;
65                 this.write_scalar(Scalar::from_i32(result), dest)?;
66             }
67
68             // Time related shims
69             "clock_gettime" => {
70                 // This is a POSIX function but it has only been tested on linux.
71                 let &[clk_id, tp] = check_arg_count(args)?;
72                 let result = this.clock_gettime(clk_id, tp)?;
73                 this.write_scalar(Scalar::from_i32(result), dest)?;
74             }
75
76             // Querying system information
77             "pthread_attr_getstack" => {
78                 // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
79                 let &[attr_place, addr_place, size_place] = check_arg_count(args)?;
80                 this.deref_operand(attr_place)?;
81                 let addr_place = this.deref_operand(addr_place)?;
82                 let size_place = this.deref_operand(size_place)?;
83
84                 this.write_scalar(
85                     Scalar::from_uint(STACK_ADDR, this.pointer_size()),
86                     addr_place.into(),
87                 )?;
88                 this.write_scalar(
89                     Scalar::from_uint(STACK_SIZE, this.pointer_size()),
90                     size_place.into(),
91                 )?;
92
93                 // Return success (`0`).
94                 this.write_null(dest)?;
95             }
96
97             // Threading
98             "prctl" => {
99                 let &[option, arg2, arg3, arg4, arg5] = check_arg_count(args)?;
100                 let result = this.prctl(option, arg2, arg3, arg4, arg5)?;
101                 this.write_scalar(Scalar::from_i32(result), dest)?;
102             }
103             "pthread_condattr_setclock" => {
104                 let &[attr, clock_id] = check_arg_count(args)?;
105                 let result = this.pthread_condattr_setclock(attr, clock_id)?;
106                 this.write_scalar(Scalar::from_i32(result), dest)?;
107             }
108             "pthread_condattr_getclock" => {
109                 let &[attr, clock_id] = check_arg_count(args)?;
110                 let result = this.pthread_condattr_getclock(attr, clock_id)?;
111                 this.write_scalar(Scalar::from_i32(result), dest)?;
112             }
113
114             // Dynamically invoked syscalls
115             "syscall" => {
116                 // FIXME: The libc syscall() function is a variadic function.
117                 // It's valid to call it with more arguments than a syscall
118                 // needs, so none of these syscalls should use check_arg_count.
119                 // It's even valid to call it with the wrong type of arguments,
120                 // as long as they'd end up in the same place with the calling
121                 // convention used. (E.g. using a `usize` instead of a pointer.)
122                 // It's not directly clear which number, size, and type of arguments
123                 // are acceptable in which cases and which aren't. (E.g. some
124                 // types might take up the space of two registers.)
125                 // So this needs to be researched first.
126
127                 let sys_getrandom = this
128                     .eval_libc("SYS_getrandom")?
129                     .to_machine_usize(this)?;
130
131                 let sys_statx = this
132                     .eval_libc("SYS_statx")?
133                     .to_machine_usize(this)?;
134
135                 let sys_futex = this
136                     .eval_libc("SYS_futex")?
137                     .to_machine_usize(this)?;
138
139                 if args.is_empty() {
140                     throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
141                 }
142                 match this.read_scalar(args[0])?.to_machine_usize(this)? {
143                     // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
144                     // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
145                     id if id == sys_getrandom => {
146                         // The first argument is the syscall id, so skip over it.
147                         let &[_, ptr, len, flags] = check_arg_count(args)?;
148                         getrandom(this, ptr, len, flags, dest)?;
149                     }
150                     // `statx` is used by `libstd` to retrieve metadata information on `linux`
151                     // instead of using `stat`,`lstat` or `fstat` as on `macos`.
152                     id if id == sys_statx => {
153                         // The first argument is the syscall id, so skip over it.
154                         let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
155                         let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
156                         this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
157                     }
158                     // `futex` is used by some synchonization primitives.
159                     id if id == sys_futex => {
160                         futex(this, args, dest)?;
161                     }
162                     id => throw_unsup_format!("miri does not support syscall ID {}", id),
163                 }
164             }
165
166             // Miscelanneous
167             "getrandom" => {
168                 let &[ptr, len, flags] = check_arg_count(args)?;
169                 getrandom(this, ptr, len, flags, dest)?;
170             }
171             "sched_getaffinity" => {
172                 let &[pid, cpusetsize, mask] = check_arg_count(args)?;
173                 this.read_scalar(pid)?.to_i32()?;
174                 this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
175                 this.deref_operand(mask)?;
176                 // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
177                 let einval = this.eval_libc("EINVAL")?;
178                 this.set_last_error(einval)?;
179                 this.write_scalar(Scalar::from_i32(-1), dest)?;
180             }
181
182             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
183             // These shims are enabled only when the caller is in the standard library.
184             "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
185                 let &[_thread, _attr] = check_arg_count(args)?;
186                 this.write_null(dest)?;
187             }
188
189             _ => throw_unsup_format!("can't call foreign function: {}", link_name),
190         };
191
192         Ok(true)
193     }
194 }
195
196 // Shims the linux `getrandom` syscall.
197 fn getrandom<'tcx>(
198     this: &mut MiriEvalContext<'_, 'tcx>,
199     ptr: OpTy<'tcx, Tag>,
200     len: OpTy<'tcx, Tag>,
201     flags: OpTy<'tcx, Tag>,
202     dest: PlaceTy<'tcx, Tag>,
203 ) -> InterpResult<'tcx> {
204     let ptr = this.read_scalar(ptr)?.check_init()?;
205     let len = this.read_scalar(len)?.to_machine_usize(this)?;
206
207     // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
208     // neither of which have any effect on our current PRNG.
209     this.read_scalar(flags)?.to_i32()?;
210
211     this.gen_random(ptr, len)?;
212     this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
213     Ok(())
214 }