miri/shims/unix/linux_like/
syscall.rs

1use rustc_abi::CanonAbi;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5
6use crate::helpers::check_min_vararg_count;
7use crate::shims::unix::env::EvalContextExt;
8use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
9use crate::shims::unix::linux_like::sync::futex;
10use crate::*;
11
12pub fn syscall<'tcx>(
13    ecx: &mut MiriInterpCx<'tcx>,
14    link_name: Symbol,
15    abi: &FnAbi<'tcx, Ty<'tcx>>,
16    args: &[OpTy<'tcx>],
17    dest: &MPlaceTy<'tcx>,
18) -> InterpResult<'tcx> {
19    let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?;
20    // The syscall variadic function is legal to call with more arguments than needed,
21    // extra arguments are simply ignored. The important check is that when we use an
22    // argument, we have to also check all arguments *before* it to ensure that they
23    // have the right type.
24
25    let sys_getrandom = ecx.eval_libc("SYS_getrandom").to_target_usize(ecx)?;
26    let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?;
27    let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
28    let sys_gettid = ecx.eval_libc("SYS_gettid").to_target_usize(ecx)?;
29
30    match ecx.read_target_usize(op)? {
31        // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
32        // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
33        num if num == sys_getrandom => {
34            // Used by getrandom 0.1
35            // The first argument is the syscall id, so skip over it.
36            let [ptr, len, flags] = check_min_vararg_count("syscall(SYS_getrandom, ...)", varargs)?;
37
38            let ptr = ecx.read_pointer(ptr)?;
39            let len = ecx.read_target_usize(len)?;
40            // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
41            // neither of which have any effect on our current PRNG.
42            // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
43            let _flags = ecx.read_scalar(flags)?.to_i32()?;
44
45            ecx.gen_random(ptr, len)?;
46            ecx.write_scalar(Scalar::from_target_usize(len, ecx), dest)?;
47        }
48        // `futex` is used by some synchronization primitives.
49        num if num == sys_futex => {
50            futex(ecx, varargs, dest)?;
51        }
52        num if num == sys_eventfd2 => {
53            let [initval, flags] = check_min_vararg_count("syscall(SYS_evetfd2, ...)", varargs)?;
54
55            let result = ecx.eventfd(initval, flags)?;
56            ecx.write_int(result.to_i32()?, dest)?;
57        }
58        num if num == sys_gettid => {
59            let result = ecx.unix_gettid("SYS_gettid")?;
60            ecx.write_int(result.to_u32()?, dest)?;
61        }
62        num => {
63            throw_unsup_format!("syscall: unsupported syscall number {num}");
64        }
65    };
66
67    interp_ok(())
68}