]> git.lizzy.rs Git - rust.git/blob - src/shims/posix/linux/foreign_items.rs
Add FIXME note about variadic syscall().
[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                 // However, depending on the calling convention it might depend
120                 // on the type and size of the arguments whether a call with
121                 // the wrong number of arguments (or types) is valid or not.
122                 // So this needs to be researched first.
123                 let sys_getrandom = this
124                     .eval_libc("SYS_getrandom")?
125                     .to_machine_usize(this)?;
126
127                 let sys_statx = this
128                     .eval_libc("SYS_statx")?
129                     .to_machine_usize(this)?;
130
131                 let sys_futex = this
132                     .eval_libc("SYS_futex")?
133                     .to_machine_usize(this)?;
134
135                 if args.is_empty() {
136                     throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
137                 }
138                 match this.read_scalar(args[0])?.to_machine_usize(this)? {
139                     // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
140                     // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
141                     id if id == sys_getrandom => {
142                         // The first argument is the syscall id, so skip over it.
143                         let &[_, ptr, len, flags] = check_arg_count(args)?;
144                         getrandom(this, ptr, len, flags, dest)?;
145                     }
146                     // `statx` is used by `libstd` to retrieve metadata information on `linux`
147                     // instead of using `stat`,`lstat` or `fstat` as on `macos`.
148                     id if id == sys_statx => {
149                         // The first argument is the syscall id, so skip over it.
150                         let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
151                         let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
152                         this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
153                     }
154                     // `futex` is used by some synchonization primitives.
155                     id if id == sys_futex => {
156                         futex(this, args, dest)?;
157                     }
158                     id => throw_unsup_format!("miri does not support syscall ID {}", id),
159                 }
160             }
161
162             // Miscelanneous
163             "getrandom" => {
164                 let &[ptr, len, flags] = check_arg_count(args)?;
165                 getrandom(this, ptr, len, flags, dest)?;
166             }
167             "sched_getaffinity" => {
168                 let &[pid, cpusetsize, mask] = check_arg_count(args)?;
169                 this.read_scalar(pid)?.to_i32()?;
170                 this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
171                 this.deref_operand(mask)?;
172                 // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
173                 let einval = this.eval_libc("EINVAL")?;
174                 this.set_last_error(einval)?;
175                 this.write_scalar(Scalar::from_i32(-1), dest)?;
176             }
177
178             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
179             // These shims are enabled only when the caller is in the standard library.
180             "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
181                 let &[_thread, _attr] = check_arg_count(args)?;
182                 this.write_null(dest)?;
183             }
184
185             _ => throw_unsup_format!("can't call foreign function: {}", link_name),
186         };
187
188         Ok(true)
189     }
190 }
191
192 // Shims the linux `getrandom` syscall.
193 fn getrandom<'tcx>(
194     this: &mut MiriEvalContext<'_, 'tcx>,
195     ptr: OpTy<'tcx, Tag>,
196     len: OpTy<'tcx, Tag>,
197     flags: OpTy<'tcx, Tag>,
198     dest: PlaceTy<'tcx, Tag>,
199 ) -> InterpResult<'tcx> {
200     let ptr = this.read_scalar(ptr)?.check_init()?;
201     let len = this.read_scalar(len)?.to_machine_usize(this)?;
202
203     // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
204     // neither of which have any effect on our current PRNG.
205     this.read_scalar(flags)?.to_i32()?;
206
207     this.gen_random(ptr, len)?;
208     this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
209     Ok(())
210 }