Skip to main content

miri/shims/unix/linux_like/
syscall.rs

1use rustc_middle::ty::Ty;
2use rustc_span::Symbol;
3use rustc_target::callconv::FnAbi;
4use rustc_target::spec::Arch;
5
6use crate::shims::unix::env::EvalContextExt as _;
7use crate::shims::unix::fs::EvalContextExt as _;
8use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
9use crate::shims::unix::linux_like::sync::futex;
10use crate::shims::unix::socket::EvalContextExt as _;
11use crate::*;
12
13pub fn syscall<'tcx>(
14    ecx: &mut MiriInterpCx<'tcx>,
15    link_name: Symbol,
16    abi: &FnAbi<'tcx, Ty<'tcx>>,
17    args: &[OpTy<'tcx>],
18    dest: &MPlaceTy<'tcx>,
19) -> InterpResult<'tcx> {
20    let ([op], varargs) = ecx.check_shim_sig_variadic(
21        shim_sig!(extern "C" fn(isize, ...) -> isize),
22        (link_name, abi, args),
23    )?;
24    // The syscall variadic function is legal to call with more arguments than needed,
25    // extra arguments are simply ignored. The important check is that when we use an
26    // argument, we have to also check all arguments *before* it to ensure that they
27    // have the right type.
28
29    let sys_getrandom = ecx.eval_libc("SYS_getrandom").to_target_usize(ecx)?;
30    // riscv32 only has `SYS_futex_time64`. `libc::timespec` already uses 64-bit time there, so it
31    // behaves exactly like `SYS_futex` on other targets.
32    let sys_futex = if ecx.tcx.sess.target.arch == Arch::RiscV32 {
33        ecx.eval_libc("SYS_futex_time64")
34    } else {
35        ecx.eval_libc("SYS_futex")
36    }
37    .to_target_usize(ecx)?;
38    let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
39    let sys_gettid = ecx.eval_libc("SYS_gettid").to_target_usize(ecx)?;
40    let sys_accept4 = ecx.eval_libc("SYS_accept4").to_target_usize(ecx)?;
41    let sys_copy_file_range = ecx.eval_libc("SYS_copy_file_range").to_target_usize(ecx)?;
42
43    // FIXME: Do these all have direct tests (#3179)?
44    match ecx.read_target_usize(&op)? {
45        // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
46        // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
47        num if num == sys_getrandom => {
48            // Used by getrandom 0.1
49            // The first argument is the syscall id, so skip over it.
50            let ([ptr, len, flags], _) = ecx.check_varargs(
51                shim_varargs![*libc::c_void, usize, i32],
52                varargs,
53                "syscall(SYS_getrandom, ...)",
54            )?;
55
56            let ptr = ecx.read_pointer(&ptr)?;
57            let len = ecx.read_target_usize(&len)?;
58            // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
59            // neither of which have any effect on our current PRNG.
60            // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
61            let _flags = ecx.read_scalar(&flags)?.to_i32()?;
62
63            ecx.gen_random(ptr, len)?;
64            ecx.write_scalar(Scalar::from_target_usize(len, ecx), dest)?;
65        }
66        // `futex` is used by some synchronization primitives.
67        num if num == sys_futex => {
68            futex(ecx, varargs, dest)?;
69        }
70        num if num == sys_eventfd2 => {
71            let ([initval, flags], _) =
72                ecx.check_varargs(shim_varargs![u32, i32], varargs, "syscall(SYS_evetfd2, ...)")?;
73
74            let result = ecx.eventfd(&initval, &flags)?;
75            ecx.write_int(result.to_i32()?, dest)?;
76        }
77        num if num == sys_gettid => {
78            let result = ecx.unix_gettid("SYS_gettid")?;
79            ecx.write_int(result.to_u32()?, dest)?;
80        }
81        num if num == sys_accept4 => {
82            // Used on Android.
83            let ([socket, address, address_len, flags], _) = ecx.check_varargs(
84                shim_varargs![i32, *libc::sockaddr, *libc::socklen_t, i32],
85                varargs,
86                "syscall(SYS_accept4, ...)",
87            )?;
88            ecx.accept4(&socket, &address, &address_len, Some(&flags), dest)?;
89        }
90        num if num == sys_copy_file_range => {
91            let ([fd_in, off_in, fd_out, off_out, len, flags], _) = &ecx.check_varargs(
92                shim_varargs![i32, *libc::off_t, i32, *libc::off_t, libc::size_t, u32],
93                varargs,
94                "syscall(SYS_copy_file_range, ...)",
95            )?;
96            ecx.linux_copy_file_range(fd_in, off_in, fd_out, off_out, len, flags, dest)?;
97        }
98        _ => {
99            // ENOSYS for unknown syscalls. That matches us returning null for all
100            // unknown weak symbols.
101            ecx.set_errno_and_return_neg1(LibcError("ENOSYS"), dest)?;
102        }
103    };
104
105    interp_ok(())
106}