diff --git a/src/helpers.rs b/src/helpers.rs index 644ea25fbc..510efe4660 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,4 +1,4 @@ -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::mem; use std::num::NonZeroUsize; @@ -471,6 +471,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } +/// Check that the number of args is what we expect. +pub fn check_arg_count<'a, 'tcx, const N: usize>(args: &'a [OpTy<'tcx, Tag>]) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]> + where &'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]> { + if let Ok(ops) = args.try_into() { + return Ok(ops); + } + throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) +} + pub fn immty_from_int_checked<'tcx>( int: impl Into, layout: TyAndLayout<'tcx>, diff --git a/src/lib.rs b/src/lib.rs index beee94b918..e8cf507d35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,9 @@ #![warn(rust_2018_idioms)] #![allow(clippy::cast_lossless)] +#![allow(incomplete_features)] +#![feature(const_generics)] + extern crate rustc_apfloat; extern crate rustc_ast; #[macro_use] extern crate rustc_middle; diff --git a/src/shims/dlsym.rs b/src/shims/dlsym.rs index 0f6ba63e98..3c4a942b59 100644 --- a/src/shims/dlsym.rs +++ b/src/shims/dlsym.rs @@ -1,6 +1,7 @@ use rustc_middle::mir; use crate::*; +use helpers::check_arg_count; #[derive(Debug, Copy, Clone)] pub enum Dlsym { @@ -35,8 +36,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match dlsym { GetEntropy => { - let ptr = this.read_scalar(args[0])?.not_undef()?; - let len = this.read_scalar(args[1])?.to_machine_usize(this)?; + let &[ptr, len] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let len = this.read_scalar(len)?.to_machine_usize(this)?; this.gen_random(ptr, len)?; this.write_null(dest)?; } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 25aece5989..8a75fb03a5 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -11,6 +11,7 @@ use rustc_span::symbol::sym; use rustc_ast::attr; use crate::*; +use helpers::check_arg_count; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { @@ -139,8 +140,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "exit" | "ExitProcess" => { + let &[code] = check_arg_count(args)?; // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway - let code = this.read_scalar(args[0])?.to_i32()?; + let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit(code.into())); } _ => throw_unsup_format!("can't call (diverging) foreign function: {}", link_name), @@ -197,25 +199,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // Standard C allocation "malloc" => { - let size = this.read_scalar(args[0])?.to_machine_usize(this)?; + let &[size] = check_arg_count(args)?; + let size = this.read_scalar(size)?.to_machine_usize(this)?; let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C); this.write_scalar(res, dest)?; } "calloc" => { - let items = this.read_scalar(args[0])?.to_machine_usize(this)?; - let len = this.read_scalar(args[1])?.to_machine_usize(this)?; + let &[items, len] = check_arg_count(args)?; + let items = this.read_scalar(items)?.to_machine_usize(this)?; + let len = this.read_scalar(len)?.to_machine_usize(this)?; let size = items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?; let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C); this.write_scalar(res, dest)?; } "free" => { - let ptr = this.read_scalar(args[0])?.not_undef()?; + let &[ptr] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; this.free(ptr, MiriMemoryKind::C)?; } "realloc" => { - let old_ptr = this.read_scalar(args[0])?.not_undef()?; - let new_size = this.read_scalar(args[1])?.to_machine_usize(this)?; + let &[old_ptr, new_size] = check_arg_count(args)?; + let old_ptr = this.read_scalar(old_ptr)?.not_undef()?; + let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?; this.write_scalar(res, dest)?; } @@ -224,8 +230,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // (Usually these would be forwarded to to `#[global_allocator]`; we instead implement a generic // allocation that also checks that all conditions are met, such as not permitting zero-sized allocations.) "__rust_alloc" => { - let size = this.read_scalar(args[0])?.to_machine_usize(this)?; - let align = this.read_scalar(args[1])?.to_machine_usize(this)?; + let &[size, align] = check_arg_count(args)?; + let size = this.read_scalar(size)?.to_machine_usize(this)?; + let align = this.read_scalar(align)?.to_machine_usize(this)?; Self::check_alloc_request(size, align)?; let ptr = this.memory.allocate( Size::from_bytes(size), @@ -235,8 +242,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(ptr, dest)?; } "__rust_alloc_zeroed" => { - let size = this.read_scalar(args[0])?.to_machine_usize(this)?; - let align = this.read_scalar(args[1])?.to_machine_usize(this)?; + let &[size, align] = check_arg_count(args)?; + let size = this.read_scalar(size)?.to_machine_usize(this)?; + let align = this.read_scalar(align)?.to_machine_usize(this)?; Self::check_alloc_request(size, align)?; let ptr = this.memory.allocate( Size::from_bytes(size), @@ -248,9 +256,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(ptr, dest)?; } "__rust_dealloc" => { - let ptr = this.read_scalar(args[0])?.not_undef()?; - let old_size = this.read_scalar(args[1])?.to_machine_usize(this)?; - let align = this.read_scalar(args[2])?.to_machine_usize(this)?; + let &[ptr, old_size, align] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; + let align = this.read_scalar(align)?.to_machine_usize(this)?; // No need to check old_size/align; we anyway check that they match the allocation. let ptr = this.force_ptr(ptr)?; this.memory.deallocate( @@ -260,12 +269,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx )?; } "__rust_realloc" => { - let old_size = this.read_scalar(args[1])?.to_machine_usize(this)?; - let align = this.read_scalar(args[2])?.to_machine_usize(this)?; - let new_size = this.read_scalar(args[3])?.to_machine_usize(this)?; + let &[ptr, old_size, align, new_size] = check_arg_count(args)?; + let ptr = this.force_ptr(this.read_scalar(ptr)?.not_undef()?)?; + let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; + let align = this.read_scalar(align)?.to_machine_usize(this)?; + let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; Self::check_alloc_request(new_size, align)?; // No need to check old_size; we anyway check that they match the allocation. - let ptr = this.force_ptr(this.read_scalar(args[0])?.not_undef()?)?; let align = Align::from_bytes(align).unwrap(); let new_ptr = this.memory.reallocate( ptr, @@ -279,9 +289,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // C memory handling functions "memcmp" => { - let left = this.read_scalar(args[0])?.not_undef()?; - let right = this.read_scalar(args[1])?.not_undef()?; - let n = Size::from_bytes(this.read_scalar(args[2])?.to_machine_usize(this)?); + let &[left, right, n] = check_arg_count(args)?; + let left = this.read_scalar(left)?.not_undef()?; + let right = this.read_scalar(right)?.not_undef()?; + let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?); let result = { let left_bytes = this.memory.read_bytes(left, n)?; @@ -298,9 +309,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(Scalar::from_i32(result), dest)?; } "memrchr" => { - let ptr = this.read_scalar(args[0])?.not_undef()?; - let val = this.read_scalar(args[1])?.to_i32()? as u8; - let num = this.read_scalar(args[2])?.to_machine_usize(this)?; + let &[ptr, val, num] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let val = this.read_scalar(val)?.to_i32()? as u8; + let num = this.read_scalar(num)?.to_machine_usize(this)?; if let Some(idx) = this .memory .read_bytes(ptr, Size::from_bytes(num))? @@ -315,9 +327,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } "memchr" => { - let ptr = this.read_scalar(args[0])?.not_undef()?; - let val = this.read_scalar(args[1])?.to_i32()? as u8; - let num = this.read_scalar(args[2])?.to_machine_usize(this)?; + let &[ptr, val, num] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let val = this.read_scalar(val)?.to_i32()? as u8; + let num = this.read_scalar(num)?.to_machine_usize(this)?; let idx = this .memory .read_bytes(ptr, Size::from_bytes(num))? @@ -331,7 +344,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } "strlen" => { - let ptr = this.read_scalar(args[0])?.not_undef()?; + let &[ptr] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; let n = this.memory.read_c_str(ptr)?.len(); this.write_scalar(Scalar::from_machine_usize(u64::try_from(n).unwrap(), this), dest)?; } @@ -345,8 +359,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "asinf" | "atanf" => { + let &[f] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?); + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); let f = match link_name { "cbrtf" => f.cbrt(), "coshf" => f.cosh(), @@ -363,11 +378,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "hypotf" | "atan2f" => { + let &[f1, f2] = check_arg_count(args)?; // underscore case for windows, here and below // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) // FIXME: Using host floats. - let f1 = f32::from_bits(this.read_scalar(args[0])?.to_u32()?); - let f2 = f32::from_bits(this.read_scalar(args[1])?.to_u32()?); + let f1 = f32::from_bits(this.read_scalar(f1)?.to_u32()?); + let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?); let n = match link_name { "_hypotf" | "hypotf" => f1.hypot(f2), "atan2f" => f1.atan2(f2), @@ -383,8 +399,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "asin" | "atan" => { + let &[f] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?); + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); let f = match link_name { "cbrt" => f.cbrt(), "cosh" => f.cosh(), @@ -401,9 +418,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "hypot" | "atan2" => { + let &[f1, f2] = check_arg_count(args)?; // FIXME: Using host floats. - let f1 = f64::from_bits(this.read_scalar(args[0])?.to_u64()?); - let f2 = f64::from_bits(this.read_scalar(args[1])?.to_u64()?); + let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?); + let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?); let n = match link_name { "_hypot" | "hypot" => f1.hypot(f2), "atan2" => f1.atan2(f2), @@ -415,9 +433,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "ldexp" | "scalbn" => { + let &[x, exp] = check_arg_count(args)?; // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. - let x = this.read_scalar(args[0])?.to_f64()?; - let exp = this.read_scalar(args[1])?.to_i32()?; + let x = this.read_scalar(x)?.to_f64()?; + let exp = this.read_scalar(exp)?.to_i32()?; // Saturating cast to i16. Even those are outside the valid exponent range to // `scalbn` below will do its over/underflow handling. @@ -435,6 +454,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Architecture-specific shims "llvm.x86.sse2.pause" if this.tcx.sess.target.target.arch == "x86" || this.tcx.sess.target.target.arch == "x86_64" => { + let &[] = check_arg_count(args)?; this.sched_yield()?; } diff --git a/src/shims/foreign_items/posix.rs b/src/shims/foreign_items/posix.rs index 6e2a7a9fcb..6b28e70701 100644 --- a/src/shims/foreign_items/posix.rs +++ b/src/shims/foreign_items/posix.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use log::trace; use crate::*; +use helpers::check_arg_count; use rustc_middle::mir; use rustc_target::abi::{Align, LayoutOf, Size}; @@ -23,43 +24,51 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // Environment related shims "getenv" => { - let result = this.getenv(args[0])?; + let &[name] = check_arg_count(args)?; + let result = this.getenv(name)?; this.write_scalar(result, dest)?; } "unsetenv" => { - let result = this.unsetenv(args[0])?; + let &[name] = check_arg_count(args)?; + let result = this.unsetenv(name)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "setenv" => { - let result = this.setenv(args[0], args[1])?; + let &[name, value, _overwrite] = check_arg_count(args)?; + let result = this.setenv(name, value)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "getcwd" => { - let result = this.getcwd(args[0], args[1])?; + let &[buf, size] = check_arg_count(args)?; + let result = this.getcwd(buf, size)?; this.write_scalar(result, dest)?; } "chdir" => { - let result = this.chdir(args[0])?; + let &[path] = check_arg_count(args)?; + let result = this.chdir(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // File related shims "open" | "open64" => { - let result = this.open(args[0], args[1])?; + let &[path, flag, _mode] = check_arg_count(args)?; + let result = this.open(path, flag)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "fcntl" => { - let result = this.fcntl(args[0], args[1], args.get(2).cloned())?; + let result = this.fcntl(args)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "read" => { - let result = this.read(args[0], args[1], args[2])?; + let &[fd, buf, count] = check_arg_count(args)?; + let result = this.read(fd, buf, count)?; this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } "write" => { - let fd = this.read_scalar(args[0])?.to_i32()?; - let buf = this.read_scalar(args[1])?.not_undef()?; - let n = this.read_scalar(args[2])?.to_machine_usize(this)?; + let &[fd, buf, n] = check_arg_count(args)?; + let fd = this.read_scalar(fd)?.to_i32()?; + let buf = this.read_scalar(buf)?.not_undef()?; + let n = this.read_scalar(n)?.to_machine_usize(this)?; trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); let result = if fd == 1 || fd == 2 { // stdout/stderr @@ -84,46 +93,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Err(_) => -1, } } else { - this.write(args[0], args[1], args[2])? + let &[fd, buf, count] = check_arg_count(args)?; + this.write(fd, buf, count)? }; // Now, `result` is the value we return back to the program. this.write_scalar(Scalar::from_machine_isize(result, this), dest)?; } "unlink" => { - let result = this.unlink(args[0])?; + let &[path] = check_arg_count(args)?; + let result = this.unlink(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "symlink" => { - let result = this.symlink(args[0], args[1])?; + let &[target, linkpath] = check_arg_count(args)?; + let result = this.symlink(target, linkpath)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "rename" => { - let result = this.rename(args[0], args[1])?; + let &[oldpath, newpath] = check_arg_count(args)?; + let result = this.rename(oldpath, newpath)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "mkdir" => { - let result = this.mkdir(args[0], args[1])?; + let &[path, mode] = check_arg_count(args)?; + let result = this.mkdir(path, mode)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "rmdir" => { - let result = this.rmdir(args[0])?; + let &[path] = check_arg_count(args)?; + let result = this.rmdir(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "closedir" => { - let result = this.closedir(args[0])?; + let &[dirp] = check_arg_count(args)?; + let result = this.closedir(dirp)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "lseek" | "lseek64" => { - let result = this.lseek64(args[0], args[1], args[2])?; + let &[fd, offset, whence] = check_arg_count(args)?; + let result = this.lseek64(fd, offset, whence)?; // "lseek" is only used on macOS which is 64bit-only, so `i64` always works. this.write_scalar(Scalar::from_i64(result), dest)?; } // Allocation "posix_memalign" => { - let ret = this.deref_operand(args[0])?; - let align = this.read_scalar(args[1])?.to_machine_usize(this)?; - let size = this.read_scalar(args[2])?.to_machine_usize(this)?; + let &[ret, align, size] = check_arg_count(args)?; + let ret = this.deref_operand(ret)?; + let align = this.read_scalar(align)?.to_machine_usize(this)?; + let size = this.read_scalar(size)?.to_machine_usize(this)?; // Align must be power of 2, and also at least ptr-sized (POSIX rules). if !align.is_power_of_two() { throw_ub_format!("posix_memalign: alignment must be a power of two, but is {}", align); @@ -150,8 +168,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Dynamic symbol loading "dlsym" => { - let _handle = this.read_scalar(args[0])?; - let symbol = this.read_scalar(args[1])?.not_undef()?; + let &[handle, symbol] = check_arg_count(args)?; + this.read_scalar(handle)?.not_undef()?; + let symbol = this.read_scalar(symbol)?.not_undef()?; let symbol_name = this.memory.read_c_str(symbol)?; let err = format!("bad c unicode symbol: {:?}", symbol_name); let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err); @@ -165,7 +184,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Querying system information "sysconf" => { - let name = this.read_scalar(args[0])?.to_i32()?; + let &[name] = check_arg_count(args)?; + let name = this.read_scalar(name)?.to_i32()?; let sysconfs = &[ ("_SC_PAGESIZE", Scalar::from_int(PAGE_SIZE, this.pointer_size())), @@ -188,17 +208,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Thread-local storage "pthread_key_create" => { - let key_place = this.deref_operand(args[0])?; + let &[key, dtor] = check_arg_count(args)?; + let key_place = this.deref_operand(key)?; + let dtor = this.read_scalar(dtor)?.not_undef()?; // Extract the function type out of the signature (that seems easier than constructing it ourselves). - let dtor = match this.test_null(this.read_scalar(args[1])?.not_undef()?)? { + let dtor = match this.test_null(dtor)? { Some(dtor_ptr) => Some(this.memory.get_fn(dtor_ptr)?.as_instance()?), None => None, }; // Figure out how large a pthread TLS key actually is. // To this end, deref the argument type. This is `libc::pthread_key_t`. - let key_type = args[0].layout.ty + let key_type = key.layout.ty .builtin_deref(true) .ok_or_else(|| err_ub_format!( "wrong signature used for `pthread_key_create`: first argument must be a raw pointer." @@ -214,21 +236,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "pthread_key_delete" => { - let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?; + let &[key] = check_arg_count(args)?; + let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?; this.machine.tls.delete_tls_key(key)?; // Return success (0) this.write_null(dest)?; } "pthread_getspecific" => { - let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?; + let &[key] = check_arg_count(args)?; + let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?; let active_thread = this.get_active_thread()?; let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { - let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?; + let &[key, new_ptr] = check_arg_count(args)?; + let key = this.force_bits(this.read_scalar(key)?.not_undef()?, key.layout.size)?; let active_thread = this.get_active_thread()?; - let new_ptr = this.read_scalar(args[1])?.not_undef()?; + let new_ptr = this.read_scalar(new_ptr)?.not_undef()?; this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?; // Return success (`0`). @@ -237,91 +262,106 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Synchronization primitives "pthread_mutexattr_init" => { - let result = this.pthread_mutexattr_init(args[0])?; + let &[attr] = check_arg_count(args)?; + let result = this.pthread_mutexattr_init(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutexattr_settype" => { - let result = this.pthread_mutexattr_settype(args[0], args[1])?; + let &[attr, kind] = check_arg_count(args)?; + let result = this.pthread_mutexattr_settype(attr, kind)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutexattr_destroy" => { - let result = this.pthread_mutexattr_destroy(args[0])?; + let &[attr] = check_arg_count(args)?; + let result = this.pthread_mutexattr_destroy(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_init" => { - let result = this.pthread_mutex_init(args[0], args[1])?; + let &[mutex, attr] = check_arg_count(args)?; + let result = this.pthread_mutex_init(mutex, attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_lock" => { - let result = this.pthread_mutex_lock(args[0])?; + let &[mutex] = check_arg_count(args)?; + let result = this.pthread_mutex_lock(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_trylock" => { - let result = this.pthread_mutex_trylock(args[0])?; + let &[mutex] = check_arg_count(args)?; + let result = this.pthread_mutex_trylock(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_unlock" => { - let result = this.pthread_mutex_unlock(args[0])?; + let &[mutex] = check_arg_count(args)?; + let result = this.pthread_mutex_unlock(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_mutex_destroy" => { - let result = this.pthread_mutex_destroy(args[0])?; + let &[mutex] = check_arg_count(args)?; + let result = this.pthread_mutex_destroy(mutex)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_rdlock" => { - let result = this.pthread_rwlock_rdlock(args[0])?; + let &[rwlock] = check_arg_count(args)?; + let result = this.pthread_rwlock_rdlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_tryrdlock" => { - let result = this.pthread_rwlock_tryrdlock(args[0])?; + let &[rwlock] = check_arg_count(args)?; + let result = this.pthread_rwlock_tryrdlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_wrlock" => { - let result = this.pthread_rwlock_wrlock(args[0])?; + let &[rwlock] = check_arg_count(args)?; + let result = this.pthread_rwlock_wrlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_trywrlock" => { - let result = this.pthread_rwlock_trywrlock(args[0])?; + let &[rwlock] = check_arg_count(args)?; + let result = this.pthread_rwlock_trywrlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_unlock" => { - let result = this.pthread_rwlock_unlock(args[0])?; + let &[rwlock] = check_arg_count(args)?; + let result = this.pthread_rwlock_unlock(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_rwlock_destroy" => { - let result = this.pthread_rwlock_destroy(args[0])?; + let &[rwlock] = check_arg_count(args)?; + let result = this.pthread_rwlock_destroy(rwlock)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Threading "pthread_create" => { - assert_eq!(args.len(), 4); - let result = this.pthread_create(args[0], args[1], args[2], args[3])?; + let &[thread, attr, start, arg] = check_arg_count(args)?; + let result = this.pthread_create(thread, attr, start, arg)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_join" => { - assert_eq!(args.len(), 2); - let result = this.pthread_join(args[0], args[1])?; + let &[thread, retval] = check_arg_count(args)?; + let result = this.pthread_join(thread, retval)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_detach" => { - assert_eq!(args.len(), 1); - let result = this.pthread_detach(args[0])?; + let &[thread] = check_arg_count(args)?; + let result = this.pthread_detach(thread)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "pthread_self" => { - assert_eq!(args.len(), 0); + let &[] = check_arg_count(args)?; this.pthread_self(dest)?; } "sched_yield" => { - assert_eq!(args.len(), 0); + let &[] = check_arg_count(args)?; let result = this.sched_yield()?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Miscellaneous "isatty" => { - let _fd = this.read_scalar(args[0])?.to_i32()?; + let &[fd] = check_arg_count(args)?; + this.read_scalar(fd)?.to_i32()?; // "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" // FIXME: we just say nothing is a terminal. let enotty = this.eval_libc("ENOTTY")?; @@ -329,9 +369,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_null(dest)?; } "pthread_atfork" => { - let _prepare = this.read_scalar(args[0])?.not_undef()?; - let _parent = this.read_scalar(args[1])?.not_undef()?; - let _child = this.read_scalar(args[1])?.not_undef()?; + let &[prepare, parent, child] = check_arg_count(args)?; + this.read_scalar(prepare)?.not_undef()?; + this.read_scalar(parent)?.not_undef()?; + this.read_scalar(child)?.not_undef()?; // We do not support forking, so there is nothing to do here. this.write_null(dest)?; } @@ -351,7 +392,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "pthread_attr_getguardsize" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { - let guard_size = this.deref_operand(args[1])?; + let &[_attr, guard_size] = check_arg_count(args)?; + let guard_size = this.deref_operand(guard_size)?; let guard_size_layout = this.libc_ty_layout("size_t")?; this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), guard_size.into())?; diff --git a/src/shims/foreign_items/posix/linux.rs b/src/shims/foreign_items/posix/linux.rs index eb58f74660..e0f54cac15 100644 --- a/src/shims/foreign_items/posix/linux.rs +++ b/src/shims/foreign_items/posix/linux.rs @@ -1,4 +1,5 @@ use crate::*; +use helpers::check_arg_count; use rustc_middle::mir; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} @@ -15,6 +16,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // errno "__errno_location" => { + let &[] = check_arg_count(args)?; let errno_place = this.machine.last_error.unwrap(); this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; } @@ -23,27 +25,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // These symbols have different names on Linux and macOS, which is the only reason they are not // in the `posix` module. "close" => { - let result = this.close(args[0])?; + let &[fd] = check_arg_count(args)?; + let result = this.close(fd)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "opendir" => { - let result = this.opendir(args[0])?; + let &[name] = check_arg_count(args)?; + let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir64_r" => { - let result = this.linux_readdir64_r(args[0], args[1], args[2])?; + let &[dirp, entry, result] = check_arg_count(args)?; + let result = this.linux_readdir64_r(dirp, entry, result)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "ftruncate64" => { - let result = this.ftruncate64(args[0], args[1])?; + let &[fd, length] = check_arg_count(args)?; + let result = this.ftruncate64(fd, length)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Linux-only "posix_fadvise" => { - let _fd = this.read_scalar(args[0])?.to_i32()?; - let _offset = this.read_scalar(args[1])?.to_machine_isize(this)?; - let _len = this.read_scalar(args[2])?.to_machine_isize(this)?; - let _advice = this.read_scalar(args[3])?.to_i32()?; + let &[fd, offset, len, advice] = check_arg_count(args)?; + this.read_scalar(fd)?.to_i32()?; + this.read_scalar(offset)?.to_machine_isize(this)?; + this.read_scalar(len)?.to_machine_isize(this)?; + this.read_scalar(advice)?.to_i32()?; // fadvise is only informational, we can ignore it. this.write_null(dest)?; } @@ -51,16 +58,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Time related shims "clock_gettime" => { // This is a POSIX function but it has only been tested on linux. - let result = this.clock_gettime(args[0], args[1])?; + let &[clk_id, tp] = check_arg_count(args)?; + let result = this.clock_gettime(clk_id, tp)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Querying system information "pthread_attr_getstack" => { // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. - let _attr_place = this.deref_operand(args[0])?; - let addr_place = this.deref_operand(args[1])?; - let size_place = this.deref_operand(args[2])?; + let &[attr_place, addr_place, size_place] = check_arg_count(args)?; + this.deref_operand(attr_place)?; + let addr_place = this.deref_operand(addr_place)?; + let size_place = this.deref_operand(size_place)?; this.write_scalar( Scalar::from_uint(STACK_ADDR, this.pointer_size()), @@ -77,8 +86,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Threading "prctl" => { - assert_eq!(args.len(), 5); - let result = this.prctl(args[0], args[1], args[2], args[3], args[4])?; + let &[option, arg2, arg3, arg4, arg5] = check_arg_count(args)?; + let result = this.prctl(option, arg2, arg3, arg4, arg5)?; this.write_scalar(Scalar::from_i32(result), dest)?; } @@ -92,18 +101,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx .eval_libc("SYS_statx")? .to_machine_usize(this)?; + if args.is_empty() { + throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1"); + } match this.read_scalar(args[0])?.to_machine_usize(this)? { // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). id if id == sys_getrandom => { // The first argument is the syscall id, so skip over it. - getrandom(this, &args[1..], dest)?; + let &[_, ptr, len, flags] = check_arg_count(args)?; + getrandom(this, ptr, len, flags, dest)?; } // `statx` is used by `libstd` to retrieve metadata information on `linux` // instead of using `stat`,`lstat` or `fstat` as on `macos`. id if id == sys_statx => { // The first argument is the syscall id, so skip over it. - let result = this.linux_statx(args[1], args[2], args[3], args[4], args[5])?; + let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?; + let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?; } id => throw_unsup_format!("miri does not support syscall ID {}", id), @@ -112,12 +126,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Miscelanneous "getrandom" => { - getrandom(this, args, dest)?; + let &[ptr, len, flags] = check_arg_count(args)?; + getrandom(this, ptr, len, flags, dest)?; } "sched_getaffinity" => { - let _pid = this.read_scalar(args[0])?.to_i32()?; - let _cpusetsize = this.read_scalar(args[1])?.to_machine_usize(this)?; - let _mask = this.deref_operand(args[2])?; + let &[pid, cpusetsize, mask] = check_arg_count(args)?; + this.read_scalar(pid)?.to_i32()?; + this.read_scalar(cpusetsize)?.to_machine_usize(this)?; + this.deref_operand(mask)?; // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`. let einval = this.eval_libc("EINVAL")?; this.set_last_error(einval)?; @@ -127,6 +143,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { + let &[_thread, _attr] = check_arg_count(args)?; this.write_null(dest)?; } @@ -140,15 +157,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Shims the linux `getrandom` syscall. fn getrandom<'tcx>( this: &mut MiriEvalContext<'_, 'tcx>, - args: &[OpTy<'tcx, Tag>], + ptr: OpTy<'tcx, Tag>, + len: OpTy<'tcx, Tag>, + flags: OpTy<'tcx, Tag>, dest: PlaceTy<'tcx, Tag>, ) -> InterpResult<'tcx> { - let ptr = this.read_scalar(args[0])?.not_undef()?; - let len = this.read_scalar(args[1])?.to_machine_usize(this)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let len = this.read_scalar(len)?.to_machine_usize(this)?; // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, // neither of which have any effect on our current PRNG. - let _flags = this.read_scalar(args[2])?.to_i32()?; + this.read_scalar(flags)?.to_i32()?; this.gen_random(ptr, len)?; this.write_scalar(Scalar::from_machine_usize(len, this), dest)?; diff --git a/src/shims/foreign_items/posix/macos.rs b/src/shims/foreign_items/posix/macos.rs index 3677960fd8..1cfecbc934 100644 --- a/src/shims/foreign_items/posix/macos.rs +++ b/src/shims/foreign_items/posix/macos.rs @@ -1,4 +1,5 @@ use crate::*; +use helpers::check_arg_count; use rustc_middle::mir; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} @@ -15,100 +16,119 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // errno "__error" => { + let &[] = check_arg_count(args)?; let errno_place = this.machine.last_error.unwrap(); this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?; } // File related shims "close$NOCANCEL" => { - let result = this.close(args[0])?; + let &[result] = check_arg_count(args)?; + let result = this.close(result)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "stat$INODE64" => { - let result = this.macos_stat(args[0], args[1])?; + let &[path, buf] = check_arg_count(args)?; + let result = this.macos_stat(path, buf)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "lstat$INODE64" => { - let result = this.macos_lstat(args[0], args[1])?; + let &[path, buf] = check_arg_count(args)?; + let result = this.macos_lstat(path, buf)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "fstat$INODE64" => { - let result = this.macos_fstat(args[0], args[1])?; + let &[fd, buf] = check_arg_count(args)?; + let result = this.macos_fstat(fd, buf)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "opendir$INODE64" => { - let result = this.opendir(args[0])?; + let &[name] = check_arg_count(args)?; + let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir_r$INODE64" => { - let result = this.macos_readdir_r(args[0], args[1], args[2])?; + let &[dirp, entry, result] = check_arg_count(args)?; + let result = this.macos_readdir_r(dirp, entry, result)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "ftruncate" => { - let result = this.ftruncate64(args[0], args[1])?; + let &[fd, length] = check_arg_count(args)?; + let result = this.ftruncate64(fd, length)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Environment related shims "_NSGetEnviron" => { + let &[] = check_arg_count(args)?; this.write_scalar(this.machine.env_vars.environ.unwrap().ptr, dest)?; } // Time related shims "gettimeofday" => { - let result = this.gettimeofday(args[0], args[1])?; + let &[tv, tz] = check_arg_count(args)?; + let result = this.gettimeofday(tv, tz)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "mach_absolute_time" => { + let &[] = check_arg_count(args)?; let result = this.mach_absolute_time()?; this.write_scalar(Scalar::from_u64(result), dest)?; } "mach_timebase_info" => { - let result = this.mach_timebase_info(args[0])?; + let &[info] = check_arg_count(args)?; + let result = this.mach_timebase_info(info)?; this.write_scalar(Scalar::from_i32(result), dest)?; }, // Access to command-line arguments "_NSGetArgc" => { + let &[] = check_arg_count(args)?; this.write_scalar(this.machine.argc.expect("machine must be initialized"), dest)?; } "_NSGetArgv" => { + let &[] = check_arg_count(args)?; this.write_scalar(this.machine.argv.expect("machine must be initialized"), dest)?; } // Thread-local storage "_tlv_atexit" => { - let dtor = this.read_scalar(args[0])?.not_undef()?; + let &[dtor, data] = check_arg_count(args)?; + let dtor = this.read_scalar(dtor)?.not_undef()?; let dtor = this.memory.get_fn(dtor)?.as_instance()?; - let data = this.read_scalar(args[1])?.not_undef()?; + let data = this.read_scalar(data)?.not_undef()?; let active_thread = this.get_active_thread()?; this.machine.tls.set_macos_thread_dtor(active_thread, dtor, data)?; } // Querying system information "pthread_get_stackaddr_np" => { - let _thread = this.read_scalar(args[0])?.not_undef()?; + let &[thread] = check_arg_count(args)?; + this.read_scalar(thread)?.not_undef()?; let stack_addr = Scalar::from_uint(STACK_ADDR, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { - let _thread = this.read_scalar(args[0])?.not_undef()?; + let &[thread] = check_arg_count(args)?; + this.read_scalar(thread)?.not_undef()?; let stack_size = Scalar::from_uint(STACK_SIZE, this.pointer_size()); this.write_scalar(stack_size, dest)?; } // Threading "pthread_setname_np" => { - let ptr = this.read_scalar(args[0])?.not_undef()?; - this.pthread_setname_np(ptr)?; + let &[name] = check_arg_count(args)?; + let name = this.read_scalar(name)?.not_undef()?; + this.pthread_setname_np(name)?; } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "mmap" if this.frame().instance.to_string().starts_with("std::sys::unix::") => { // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value. - let addr = this.read_scalar(args[0])?.not_undef()?; + let &[addr, _, _, _, _, _] = check_arg_count(args)?; + let addr = this.read_scalar(addr)?.not_undef()?; this.write_scalar(addr, dest)?; } diff --git a/src/shims/foreign_items/windows.rs b/src/shims/foreign_items/windows.rs index f55b5b8450..ab912476f6 100644 --- a/src/shims/foreign_items/windows.rs +++ b/src/shims/foreign_items/windows.rs @@ -4,6 +4,7 @@ use rustc_middle::mir; use rustc_target::abi::Size; use crate::*; +use helpers::check_arg_count; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { @@ -23,42 +24,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx match link_name { // Environment related shims "GetEnvironmentVariableW" => { - let result = this.GetEnvironmentVariableW(args[0], args[1], args[2])?; + let &[name, buf, size] = check_arg_count(args)?; + let result = this.GetEnvironmentVariableW(name, buf, size)?; this.write_scalar(Scalar::from_u32(result), dest)?; } "SetEnvironmentVariableW" => { - let result = this.SetEnvironmentVariableW(args[0], args[1])?; + let &[name, value] = check_arg_count(args)?; + let result = this.SetEnvironmentVariableW(name, value)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "GetEnvironmentStringsW" => { + let &[] = check_arg_count(args)?; let result = this.GetEnvironmentStringsW()?; this.write_scalar(result, dest)?; } "FreeEnvironmentStringsW" => { - let result = this.FreeEnvironmentStringsW(args[0])?; + let &[env_block] = check_arg_count(args)?; + let result = this.FreeEnvironmentStringsW(env_block)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "GetCurrentDirectoryW" => { - let result = this.GetCurrentDirectoryW(args[0], args[1])?; + let &[size, buf] = check_arg_count(args)?; + let result = this.GetCurrentDirectoryW(size, buf)?; this.write_scalar(Scalar::from_u32(result), dest)?; } "SetCurrentDirectoryW" => { - let result = this.SetCurrentDirectoryW(args[0])?; + let &[path] = check_arg_count(args)?; + let result = this.SetCurrentDirectoryW(path)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // File related shims "GetStdHandle" => { - let which = this.read_scalar(args[0])?.to_i32()?; + let &[which] = check_arg_count(args)?; + let which = this.read_scalar(which)?.to_i32()?; // We just make this the identity function, so we know later in `WriteFile` // which one it is. this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?; } "WriteFile" => { - let handle = this.read_scalar(args[0])?.to_machine_isize(this)?; - let buf = this.read_scalar(args[1])?.not_undef()?; - let n = this.read_scalar(args[2])?.to_u32()?; - let written_place = this.deref_operand(args[3])?; + let &[handle, buf, n, written_ptr, _overlapped] = check_arg_count(args)?; + let handle = this.read_scalar(handle)?.to_machine_isize(this)?; + let buf = this.read_scalar(buf)?.not_undef()?; + let n = this.read_scalar(n)?.to_u32()?; + let written_place = this.deref_operand(written_ptr)?; // Spec says to always write `0` first. this.write_null(written_place.into())?; let written = if handle == -11 || handle == -12 { @@ -88,41 +97,48 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Allocation "HeapAlloc" => { - let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?; - let flags = this.read_scalar(args[1])?.to_u32()?; - let size = this.read_scalar(args[2])?.to_machine_usize(this)?; + let &[handle, flags, size] = check_arg_count(args)?; + this.read_scalar(handle)?.to_machine_isize(this)?; + let flags = this.read_scalar(flags)?.to_u32()?; + let size = this.read_scalar(size)?.to_machine_usize(this)?; let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap); this.write_scalar(res, dest)?; } "HeapFree" => { - let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?; - let _flags = this.read_scalar(args[1])?.to_u32()?; - let ptr = this.read_scalar(args[2])?.not_undef()?; + let &[handle, flags, ptr] = check_arg_count(args)?; + this.read_scalar(handle)?.to_machine_isize(this)?; + this.read_scalar(flags)?.to_u32()?; + let ptr = this.read_scalar(ptr)?.not_undef()?; this.free(ptr, MiriMemoryKind::WinHeap)?; this.write_scalar(Scalar::from_i32(1), dest)?; } "HeapReAlloc" => { - let _handle = this.read_scalar(args[0])?.to_machine_isize(this)?; - let _flags = this.read_scalar(args[1])?.to_u32()?; - let ptr = this.read_scalar(args[2])?.not_undef()?; - let size = this.read_scalar(args[3])?.to_machine_usize(this)?; + let &[handle, flags, ptr, size] = check_arg_count(args)?; + this.read_scalar(handle)?.to_machine_isize(this)?; + this.read_scalar(flags)?.to_u32()?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let size = this.read_scalar(size)?.to_machine_usize(this)?; let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?; this.write_scalar(res, dest)?; } // errno "SetLastError" => { - this.set_last_error(this.read_scalar(args[0])?.not_undef()?)?; + let &[error] = check_arg_count(args)?; + let error = this.read_scalar(error)?.not_undef()?; + this.set_last_error(error)?; } "GetLastError" => { + let &[] = check_arg_count(args)?; let last_error = this.get_last_error()?; this.write_scalar(last_error, dest)?; } // Querying system information "GetSystemInfo" => { - let system_info = this.deref_operand(args[0])?; + let &[system_info] = check_arg_count(args)?; + let system_info = this.deref_operand(system_info)?; // Initialize with `0`. this.memory.write_bytes( system_info.ptr, @@ -139,19 +155,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // This just creates a key; Windows does not natively support TLS destructors. // Create key and return it. + let &[] = check_arg_count(args)?; let key = this.machine.tls.create_tls_key(None, dest.layout.size)?; this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { - let key = u128::from(this.read_scalar(args[0])?.to_u32()?); + let &[key] = check_arg_count(args)?; + let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.get_active_thread()?; let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "TlsSetValue" => { - let key = u128::from(this.read_scalar(args[0])?.to_u32()?); + let &[key, new_ptr] = check_arg_count(args)?; + let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.get_active_thread()?; - let new_ptr = this.read_scalar(args[1])?.not_undef()?; + let new_ptr = this.read_scalar(new_ptr)?.not_undef()?; this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?; // Return success (`1`). @@ -160,6 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Access to command-line arguments "GetCommandLineW" => { + let &[] = check_arg_count(args)?; this.write_scalar( this.machine.cmd_line.expect("machine must be initialized"), dest, @@ -168,42 +188,52 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Time related shims "GetSystemTimeAsFileTime" => { - this.GetSystemTimeAsFileTime(args[0])?; + #[allow(non_snake_case)] + let &[LPFILETIME] = check_arg_count(args)?; + this.GetSystemTimeAsFileTime(LPFILETIME)?; } "QueryPerformanceCounter" => { - let result = this.QueryPerformanceCounter(args[0])?; + #[allow(non_snake_case)] + let &[lpPerformanceCount] = check_arg_count(args)?; + let result = this.QueryPerformanceCounter(lpPerformanceCount)?; this.write_scalar(Scalar::from_i32(result), dest)?; } "QueryPerformanceFrequency" => { - let result = this.QueryPerformanceFrequency(args[0])?; + #[allow(non_snake_case)] + let &[lpFrequency] = check_arg_count(args)?; + let result = this.QueryPerformanceFrequency(lpFrequency)?; this.write_scalar(Scalar::from_i32(result), dest)?; } // Miscellaneous "SystemFunction036" => { // The actual name of 'RtlGenRandom' - let ptr = this.read_scalar(args[0])?.not_undef()?; - let len = this.read_scalar(args[1])?.to_u32()?; + let &[ptr, len] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let len = this.read_scalar(len)?.to_u32()?; this.gen_random(ptr, len.into())?; this.write_scalar(Scalar::from_bool(true), dest)?; } "GetConsoleScreenBufferInfo" => { // `term` needs this, so we fake it. - let _console = this.read_scalar(args[0])?.to_machine_isize(this)?; - let _buffer_info = this.deref_operand(args[1])?; + let &[console, buffer_info] = check_arg_count(args)?; + this.read_scalar(console)?.to_machine_isize(this)?; + this.deref_operand(buffer_info)?; // Indicate an error. // FIXME: we should set last_error, but to what? this.write_null(dest)?; } "GetConsoleMode" => { // Windows "isatty" (in libtest) needs this, so we fake it. - let _console = this.read_scalar(args[0])?.to_machine_isize(this)?; - let _mode = this.deref_operand(args[1])?; + let &[console, mode] = check_arg_count(args)?; + this.read_scalar(console)?.to_machine_isize(this)?; + this.deref_operand(mode)?; // Indicate an error. // FIXME: we should set last_error, but to what? this.write_null(dest)?; } "SwitchToThread" => { + let &[] = check_arg_count(args)?; // Note that once Miri supports concurrency, this will need to return a nonzero // value if this call does result in switching to another thread. this.write_null(dest)?; @@ -217,17 +247,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { + let &[] = check_arg_count(args)?; // Just fake a HANDLE this.write_scalar(Scalar::from_machine_isize(1, this), dest)?; } - | "GetModuleHandleW" - | "GetProcAddress" - | "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::") + "GetModuleHandleW" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { - // Pretend these do not exist / nothing happened, by returning zero. + #[allow(non_snake_case)] + let &[_lpModuleName] = check_arg_count(args)?; + // Pretend this does not exist / nothing happened, by returning zero. + this.write_null(dest)?; + } + "GetProcAddress" if this.frame().instance.to_string().starts_with("std::sys::windows::") + => { + #[allow(non_snake_case)] + let &[_hModule, _lpProcName] = check_arg_count(args)?; + // Pretend this does not exist / nothing happened, by returning zero. + this.write_null(dest)?; + } + "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::") + => { + #[allow(non_snake_case)] + let &[_hConsoleOutput, _wAttribute] = check_arg_count(args)?; + // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } "AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { + #[allow(non_snake_case)] + let &[_First, _Handler] = check_arg_count(args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_scalar(Scalar::from_machine_usize(1, this), dest)?; } @@ -236,6 +283,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "LeaveCriticalSection" | "DeleteCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { + #[allow(non_snake_case)] + let &[_lpCriticalSection] = check_arg_count(args)?; assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported"); // Nothing to do, not even a return value. // (Windows locks are reentrant, and we have only 1 thread, @@ -243,6 +292,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "TryEnterCriticalSection" if this.frame().instance.to_string().starts_with("std::sys::windows::") => { + #[allow(non_snake_case)] + let &[_lpCriticalSection] = check_arg_count(args)?; assert_eq!(this.get_total_thread_count()?, 1, "concurrency on Windows not supported"); // There is only one thread, so this always succeeds and returns TRUE this.write_scalar(Scalar::from_i32(1), dest)?; diff --git a/src/shims/fs.rs b/src/shims/fs.rs index ea0b998c2e..ecb906f158 100644 --- a/src/shims/fs.rs +++ b/src/shims/fs.rs @@ -10,7 +10,7 @@ use rustc_target::abi::{Align, LayoutOf, Size}; use crate::stacked_borrows::Tag; use crate::*; -use helpers::{immty_from_int_checked, immty_from_uint_checked}; +use helpers::{check_arg_count, immty_from_int_checked, immty_from_uint_checked}; use shims::time::system_time_to_duration; #[derive(Debug)] @@ -322,22 +322,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn fcntl( &mut self, - fd_op: OpTy<'tcx, Tag>, - cmd_op: OpTy<'tcx, Tag>, - start_op: Option>, + args: &[OpTy<'tcx, Tag>], ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); this.check_no_isolation("fcntl")?; - let fd = this.read_scalar(fd_op)?.to_i32()?; - let cmd = this.read_scalar(cmd_op)?.to_i32()?; + if args.len() < 2 { + throw_ub_format!("incorrect number of arguments for fcntl: got {}, expected at least 2", args.len()); + } + let cmd = this.read_scalar(args[1])?.to_i32()?; // We only support getting the flags for a descriptor. if cmd == this.eval_libc_i32("F_GETFD")? { // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the // `FD_CLOEXEC` value without checking if the flag is set for the file because `std` // always sets this flag when opening a file. However we still need to check that the // file itself is open. + let &[fd, _] = check_arg_count(args)?; + let fd = this.read_scalar(fd)?.to_i32()?; if this.machine.file_handler.handles.contains_key(&fd) { Ok(this.eval_libc_i32("FD_CLOEXEC")?) } else { @@ -350,15 +352,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // because exec() isn't supported. The F_DUPFD and F_DUPFD_CLOEXEC commands only // differ in whether the FD_CLOEXEC flag is pre-set on the new file descriptor, // thus they can share the same implementation here. + let &[fd, _, start] = check_arg_count(args)?; + let fd = this.read_scalar(fd)?.to_i32()?; + let start = this.read_scalar(start)?.to_i32()?; if fd < MIN_NORMAL_FILE_FD { throw_unsup_format!("duplicating file descriptors for stdin, stdout, or stderr is not supported") } - let start_op = start_op.ok_or_else(|| { - err_unsup_format!( - "fcntl with command F_DUPFD or F_DUPFD_CLOEXEC requires a third argument" - ) - })?; - let start = this.read_scalar(start_op)?.to_i32()?; let fh = &mut this.machine.file_handler; let (file_result, writable) = match fh.handles.get(&fd) { Some(FileHandle { file, writable }) => (file.try_clone(), *writable), @@ -1064,7 +1063,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } fn ftruncate64( - &mut self, fd_op: OpTy<'tcx, Tag>, + &mut self, + fd_op: OpTy<'tcx, Tag>, length_op: OpTy<'tcx, Tag>, ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); diff --git a/src/shims/intrinsics.rs b/src/shims/intrinsics.rs index b64aeef485..a77443ef52 100644 --- a/src/shims/intrinsics.rs +++ b/src/shims/intrinsics.rs @@ -7,6 +7,7 @@ use rustc_apfloat::{Float, Round}; use rustc_target::abi::{Align, LayoutOf, Size}; use crate::*; +use helpers::check_arg_count; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { @@ -45,16 +46,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "copy" | "copy_nonoverlapping" => { + let &[src, dest, count] = check_arg_count(args)?; let elem_ty = substs.type_at(0); let elem_layout = this.layout_of(elem_ty)?; - let count = this.read_scalar(args[2])?.to_machine_usize(this)?; + let count = this.read_scalar(count)?.to_machine_usize(this)?; let elem_align = elem_layout.align.abi; let size = elem_layout.size.checked_mul(count, this) .ok_or_else(|| err_ub_format!("overflow computing total size of `{}`", intrinsic_name))?; - let src = this.read_scalar(args[0])?.not_undef()?; + let src = this.read_scalar(src)?.not_undef()?; let src = this.memory.check_ptr_access(src, size, elem_align)?; - let dest = this.read_scalar(args[1])?.not_undef()?; + let dest = this.read_scalar(dest)?.not_undef()?; let dest = this.memory.check_ptr_access(dest, size, elem_align)?; if let (Some(src), Some(dest)) = (src, dest) { @@ -68,25 +70,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "move_val_init" => { - let place = this.deref_operand(args[0])?; - this.copy_op(args[1], place.into())?; + let &[place, dest] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + this.copy_op(dest, place.into())?; } "volatile_load" => { - let place = this.deref_operand(args[0])?; + let &[place] = check_arg_count(args)?; + let place = this.deref_operand(place)?; this.copy_op(place.into(), dest)?; } "volatile_store" => { - let place = this.deref_operand(args[0])?; - this.copy_op(args[1], place.into())?; + let &[place, dest] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + this.copy_op(dest, place.into())?; } "write_bytes" => { + let &[ptr, val_byte, count] = check_arg_count(args)?; let ty = substs.type_at(0); let ty_layout = this.layout_of(ty)?; - let val_byte = this.read_scalar(args[1])?.to_u8()?; - let ptr = this.read_scalar(args[0])?.not_undef()?; - let count = this.read_scalar(args[2])?.to_machine_usize(this)?; + let val_byte = this.read_scalar(val_byte)?.to_u8()?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let count = this.read_scalar(count)?.to_machine_usize(this)?; let byte_count = ty_layout.size.checked_mul(count, this) .ok_or_else(|| err_ub_format!("overflow computing total size of `write_bytes`"))?; this.memory @@ -95,8 +101,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Pointer arithmetic "arith_offset" => { - let offset = this.read_scalar(args[1])?.to_machine_isize(this)?; - let ptr = this.read_scalar(args[0])?.not_undef()?; + let &[ptr, offset] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let offset = this.read_scalar(offset)?.to_machine_isize(this)?; let pointee_ty = substs.type_at(0); let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap(); @@ -105,8 +112,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx this.write_scalar(result_ptr, dest)?; } "offset" => { - let offset = this.read_scalar(args[1])?.to_machine_isize(this)?; - let ptr = this.read_scalar(args[0])?.not_undef()?; + let &[ptr, offset] = check_arg_count(args)?; + let ptr = this.read_scalar(ptr)?.not_undef()?; + let offset = this.read_scalar(offset)?.to_machine_isize(this)?; let result_ptr = this.pointer_offset_inbounds(ptr, substs.type_at(0), offset)?; this.write_scalar(result_ptr, dest)?; } @@ -127,8 +135,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "truncf32" | "roundf32" => { + let &[f] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?); + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); let f = match intrinsic_name { "sinf32" => f.sin(), "fabsf32" => f.abs(), @@ -163,8 +172,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "truncf64" | "roundf64" => { + let &[f] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?); + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); let f = match intrinsic_name { "sinf64" => f.sin(), "fabsf64" => f.abs(), @@ -191,8 +201,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "fdiv_fast" | "frem_fast" => { - let a = this.read_immediate(args[0])?; - let b = this.read_immediate(args[1])?; + let &[a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; let op = match intrinsic_name { "fadd_fast" => mir::BinOp::Add, "fsub_fast" => mir::BinOp::Sub, @@ -209,8 +220,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "maxnumf32" | "copysignf32" => { - let a = this.read_scalar(args[0])?.to_f32()?; - let b = this.read_scalar(args[1])?.to_f32()?; + let &[a, b] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; let res = match intrinsic_name { "minnumf32" => a.min(b), "maxnumf32" => a.max(b), @@ -225,8 +237,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "maxnumf64" | "copysignf64" => { - let a = this.read_scalar(args[0])?.to_f64()?; - let b = this.read_scalar(args[1])?.to_f64()?; + let &[a, b] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; let res = match intrinsic_name { "minnumf64" => a.min(b), "maxnumf64" => a.max(b), @@ -235,53 +248,60 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx }; this.write_scalar(Scalar::from_f64(res), dest)?; } - + "powf32" => { + let &[f, f2] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?); - let f2 = f32::from_bits(this.read_scalar(args[1])?.to_u32()?); + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?); this.write_scalar(Scalar::from_u32(f.powf(f2).to_bits()), dest)?; } "powf64" => { + let &[f, f2] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?); - let f2 = f64::from_bits(this.read_scalar(args[1])?.to_u64()?); + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?); this.write_scalar(Scalar::from_u64(f.powf(f2).to_bits()), dest)?; } "fmaf32" => { - let a = this.read_scalar(args[0])?.to_f32()?; - let b = this.read_scalar(args[1])?.to_f32()?; - let c = this.read_scalar(args[2])?.to_f32()?; + let &[a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; let res = a.mul_add(b, c).value; this.write_scalar(Scalar::from_f32(res), dest)?; } "fmaf64" => { - let a = this.read_scalar(args[0])?.to_f64()?; - let b = this.read_scalar(args[1])?.to_f64()?; - let c = this.read_scalar(args[2])?.to_f64()?; + let &[a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; let res = a.mul_add(b, c).value; this.write_scalar(Scalar::from_f64(res), dest)?; } "powif32" => { + let &[f, i] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?); - let i = this.read_scalar(args[1])?.to_i32()?; + let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let i = this.read_scalar(i)?.to_i32()?; this.write_scalar(Scalar::from_u32(f.powi(i).to_bits()), dest)?; } "powif64" => { + let &[f, i] = check_arg_count(args)?; // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?); - let i = this.read_scalar(args[1])?.to_i32()?; + let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let i = this.read_scalar(i)?.to_i32()?; this.write_scalar(Scalar::from_u64(f.powi(i).to_bits()), dest)?; } "float_to_int_unchecked" => { - let val = this.read_immediate(args[0])?; + let &[val] = check_arg_count(args)?; + let val = this.read_immediate(val)?; let res = match val.layout.ty.kind { ty::Float(FloatTy::F32) => { @@ -302,7 +322,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "atomic_load_relaxed" | "atomic_load_acq" => { - let place = this.deref_operand(args[0])?; + let &[place] = check_arg_count(args)?; + let place = this.deref_operand(place)?; let val = this.read_scalar(place.into())?; // make sure it fits into a scalar; otherwise it cannot be atomic // Check alignment requirements. Atomics must always be aligned to their size, @@ -319,8 +340,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "atomic_store_relaxed" | "atomic_store_rel" => { - let place = this.deref_operand(args[0])?; - let val = this.read_scalar(args[1])?; // make sure it fits into a scalar; otherwise it cannot be atomic + let &[place, val] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let val = this.read_scalar(val)?; // make sure it fits into a scalar; otherwise it cannot be atomic // Check alignment requirements. Atomics must always be aligned to their size, // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must @@ -345,8 +367,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } _ if intrinsic_name.starts_with("atomic_xchg") => { - let place = this.deref_operand(args[0])?; - let new = this.read_scalar(args[1])?; + let &[place, new] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let new = this.read_scalar(new)?; let old = this.read_scalar(place.into())?; // Check alignment requirements. Atomics must always be aligned to their size, @@ -360,9 +383,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } _ if intrinsic_name.starts_with("atomic_cxchg") => { - let place = this.deref_operand(args[0])?; - let expect_old = this.read_immediate(args[1])?; // read as immediate for the sake of `binary_op()` - let new = this.read_scalar(args[2])?; + let &[place, expect_old, new] = check_arg_count(args)?; + let place = this.deref_operand(place)?; + let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()` + let new = this.read_scalar(new)?; let old = this.read_immediate(place.into())?; // read as immediate for the sake of `binary_op()` // Check alignment requirements. Atomics must always be aligned to their size, @@ -414,11 +438,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => { - let place = this.deref_operand(args[0])?; + let &[place, rhs] = check_arg_count(args)?; + let place = this.deref_operand(place)?; if !place.layout.ty.is_integral() { bug!("Atomic arithmetic operations only work on integer types"); } - let rhs = this.read_immediate(args[1])?; + let rhs = this.read_immediate(rhs)?; let old = this.read_immediate(place.into())?; // Check alignment requirements. Atomics must always be aligned to their size, @@ -447,6 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" => { + let &[] = check_arg_count(args)?; let ty = substs.type_at(0); let layout = this.layout_of(ty)?; // Abort here because the caller might not be panic safe. @@ -462,7 +488,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "min_align_of_val" => { - let mplace = this.deref_operand(args[0])?; + let &[mplace] = check_arg_count(args)?; + let mplace = this.deref_operand(mplace)?; let (_, align) = this .size_and_align_of_mplace(mplace)? .expect("size_of_val called on extern type"); @@ -470,7 +497,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } "size_of_val" => { - let mplace = this.deref_operand(args[0])?; + let &[mplace] = check_arg_count(args)?; + let mplace = this.deref_operand(mplace)?; let (size, _) = this .size_and_align_of_mplace(mplace)? .expect("size_of_val called on extern type"); @@ -479,17 +507,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Other "assume" => { - let cond = this.read_scalar(args[0])?.to_bool()?; + let &[cond] = check_arg_count(args)?; + let cond = this.read_scalar(cond)?.not_undef()?.to_bool()?; if !cond { throw_ub_format!("`assume` intrinsic called with `false`"); } } - "exact_div" => - this.exact_div(this.read_immediate(args[0])?, this.read_immediate(args[1])?, dest)?, + "exact_div" => { + let &[num, denom] = check_arg_count(args)?; + this.exact_div(this.read_immediate(num)?, this.read_immediate(denom)?, dest)?; + } "forget" => { // We get an argument... and forget about it. + let &[_] = check_arg_count(args)?; } #[rustfmt::skip] @@ -497,7 +529,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx | "unlikely" => { // These just return their argument - let b = this.read_immediate(args[0])?; + let &[b] = check_arg_count(args)?; + let b = this.read_immediate(b)?; this.write_immediate(*b, dest)?; } diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 166d1a5456..cd525e173e 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -17,6 +17,7 @@ use log::trace; use rustc_middle::{mir, ty}; use crate::*; +use helpers::check_arg_count; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { @@ -32,7 +33,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // There are some more lang items we want to hook that CTFE does not hook (yet). if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) { - this.align_offset(args[0], args[1], ret, unwind)?; + let &[ptr, align] = check_arg_count(args)?; + this.align_offset(ptr, align, ret, unwind)?; return Ok(None); } diff --git a/src/shims/panic.rs b/src/shims/panic.rs index c926046a04..43f90f1b04 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -17,6 +17,7 @@ use rustc_middle::{mir, ty}; use rustc_target::spec::PanicStrategy; use crate::*; +use helpers::check_arg_count; /// Holds all of the relevant data for when unwinding hits a `try` frame. #[derive(Debug)] @@ -53,7 +54,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx trace!("miri_start_panic: {:?}", this.frame().instance); // Get the raw pointer stored in arg[0] (the panic payload). - let payload = this.read_scalar(args[0])?.not_undef()?; + let &[payload] = check_arg_count(args)?; + let payload = this.read_scalar(payload)?.not_undef()?; assert!( this.machine.panic_payload.is_none(), "the panic runtime should avoid double-panics" @@ -86,9 +88,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // a pointer to `Box`. // Get all the arguments. - let try_fn = this.read_scalar(args[0])?.not_undef()?; - let data = this.read_scalar(args[1])?.not_undef()?; - let catch_fn = this.read_scalar(args[2])?.not_undef()?; + let &[try_fn, data, catch_fn] = check_arg_count(args)?; + let try_fn = this.read_scalar(try_fn)?.not_undef()?; + let data = this.read_scalar(data)?.not_undef()?; + let catch_fn = this.read_scalar(catch_fn)?.not_undef()?; // Now we make a function call, and pass `data` as first and only argument. let f_instance = this.memory.get_fn(try_fn)?.as_instance()?; diff --git a/src/shims/thread.rs b/src/shims/thread.rs index ac1bb39a69..3ea1ee0aa1 100644 --- a/src/shims/thread.rs +++ b/src/shims/thread.rs @@ -121,12 +121,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn pthread_setname_np( &mut self, - ptr: Scalar, + name: Scalar, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); this.assert_target_os("macos", "pthread_setname_np"); - let name = this.memory.read_c_str(ptr)?.to_owned(); + let name = this.memory.read_c_str(name)?.to_owned(); this.set_active_thread_name(name)?; Ok(()) diff --git a/tests/compile-fail/check_arg_count_too_few_args.rs b/tests/compile-fail/check_arg_count_too_few_args.rs new file mode 100644 index 0000000000..c6c19e042d --- /dev/null +++ b/tests/compile-fail/check_arg_count_too_few_args.rs @@ -0,0 +1,12 @@ +#![feature(core_intrinsics)] +#![feature(rustc_private)] + +fn main() { + extern "C" { + fn malloc() -> *mut std::ffi::c_void; + } + + unsafe { + let _ = malloc(); //~ ERROR Undefined Behavior: incorrect number of arguments: got 0, expected 1 + }; +} diff --git a/tests/compile-fail/check_arg_count_too_many_args.rs b/tests/compile-fail/check_arg_count_too_many_args.rs new file mode 100644 index 0000000000..cca03e53ec --- /dev/null +++ b/tests/compile-fail/check_arg_count_too_many_args.rs @@ -0,0 +1,12 @@ +#![feature(core_intrinsics)] +#![feature(rustc_private)] + +fn main() { + extern "C" { + fn malloc(_: i32, _: i32) -> *mut std::ffi::c_void; + } + + unsafe { + let _ = malloc(1, 2); //~ ERROR Undefined Behavior: incorrect number of arguments: got 2, expected 1 + }; +}