From de4eea9d49b5d4d5e10e67e1ae2754bbb720564c Mon Sep 17 00:00:00 2001 From: Nym Seddon Date: Fri, 22 Jan 2021 02:45:39 +0000 Subject: [PATCH] Add ABI check for shims --- src/helpers.rs | 10 ++++++++ src/machine.rs | 8 +++--- src/shims/dlsym.rs | 6 +++-- src/shims/foreign_items.rs | 39 ++++++++++++++++++++++++++---- src/shims/mod.rs | 4 ++- src/shims/posix/dlsym.rs | 4 +++ src/shims/posix/foreign_items.rs | 6 ++++- src/shims/windows/dlsym.rs | 6 ++++- src/shims/windows/foreign_items.rs | 6 ++++- 9 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 4c989db0170..6a12a8f6ba3 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -9,6 +9,7 @@ use rustc_middle::ty::{self, List, TyCtxt, layout::TyAndLayout}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_target::abi::{LayoutOf, Size, FieldsShape, Variants}; +use rustc_target::spec::abi::Abi; use rand::RngCore; @@ -553,6 +554,15 @@ fn read_timespec( throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) } +/// Check that the ABI is what we expect. +pub fn check_abi<'a>(abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { + if abi == exp_abi { + Ok(()) + } else { + throw_ub_format!("calling a function with ABI {:?} using caller ABI {:?}", exp_abi, abi) + } +} + pub fn isolation_error(name: &str) -> InterpResult<'static> { throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( "{} not available when isolation is enabled", diff --git a/src/machine.rs b/src/machine.rs index d28aa34c75e..ab7abebaeab 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -353,24 +353,24 @@ fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - _abi: Abi, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, unwind: Option, ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> { - ecx.find_mir_or_eval_fn(instance, args, ret, unwind) + ecx.find_mir_or_eval_fn(instance, abi, args, ret, unwind) } #[inline(always)] fn call_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Dlsym, - _abi: Abi, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, _unwind: Option, ) -> InterpResult<'tcx> { - ecx.call_dlsym(fn_val, args, ret) + ecx.call_dlsym(fn_val, abi, args, ret) } #[inline(always)] diff --git a/src/shims/dlsym.rs b/src/shims/dlsym.rs index 9b15cb9ac9a..a87d8a01757 100644 --- a/src/shims/dlsym.rs +++ b/src/shims/dlsym.rs @@ -1,4 +1,5 @@ use rustc_middle::mir; +use rustc_target::spec::abi::Abi; use crate::*; use shims::posix::dlsym as posix; @@ -29,13 +30,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_dlsym( &mut self, dlsym: Dlsym, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); match dlsym { - Dlsym::Posix(dlsym) => posix::EvalContextExt::call_dlsym(this, dlsym, args, ret), - Dlsym::Windows(dlsym) => windows::EvalContextExt::call_dlsym(this, dlsym, args, ret), + Dlsym::Posix(dlsym) => posix::EvalContextExt::call_dlsym(this, dlsym, abi, args, ret), + Dlsym::Windows(dlsym) => windows::EvalContextExt::call_dlsym(this, dlsym, abi, args, ret), } } } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 237fb27d922..628e9b69e17 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -4,14 +4,14 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir; -use rustc_target::{abi::{Align, Size}, spec::PanicStrategy}; +use rustc_target::{abi::{Align, Size}, spec::{PanicStrategy, abi::Abi}}; use rustc_middle::ty; use rustc_apfloat::Float; use rustc_span::symbol::sym; use crate::*; use super::backtrace::EvalContextExt as _; -use helpers::check_arg_count; +use helpers::{check_abi, 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> { @@ -112,6 +112,7 @@ fn realloc( fn emulate_foreign_item( &mut self, def_id: DefId, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, unwind: Option, @@ -130,12 +131,14 @@ fn emulate_foreign_item( let (dest, ret) = match ret { None => match link_name { "miri_start_panic" => { + check_abi(abi, Abi::Rust)?; this.handle_miri_start_panic(args, unwind)?; return Ok(None); } // This matches calls to the foreign item `panic_impl`. // The implementation is provided by the function with the `#[panic_handler]` attribute. "panic_impl" => { + check_abi(abi, Abi::Rust)?; let panic_impl_id = tcx.lang_items().panic_impl().unwrap(); let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id); return Ok(Some(&*this.load_mir(panic_impl_instance.def, None)?)); @@ -143,12 +146,14 @@ fn emulate_foreign_item( | "exit" | "ExitProcess" => { + check_abi(abi, if link_name == "exit" { Abi::C } else { Abi::System })?; 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(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit(code.into())); } "abort" => { + check_abi(abi, Abi::C)?; throw_machine_stop!(TerminationInfo::Abort("the program aborted execution".to_owned())) } _ => throw_unsup_format!("can't call (diverging) foreign function: {}", link_name), @@ -165,6 +170,7 @@ fn emulate_foreign_item( // Normally, this will be either `libpanic_unwind` or `libpanic_abort`, but it could // also be a custom user-provided implementation via `#![feature(panic_runtime)]` "__rust_start_panic" | "__rust_panic_cleanup" => { + check_abi(abi, Abi::C)?; // This replicates some of the logic in `inject_panic_runtime`. // FIXME: is there a way to reuse that logic? let panic_runtime = match this.tcx.sess.panic_strategy() { @@ -179,7 +185,7 @@ fn emulate_foreign_item( } // Third: functions that return. - if this.emulate_foreign_item_by_name(link_name, args, dest, ret)? { + if this.emulate_foreign_item_by_name(link_name, abi, args, dest, ret)? { trace!("{:?}", this.dump_place(*dest)); this.go_to_block(ret); } @@ -193,6 +199,7 @@ fn emulate_foreign_item( fn emulate_foreign_item_by_name( &mut self, link_name: &str, + abi: Abi, args: &[OpTy<'tcx, Tag>], dest: PlaceTy<'tcx, Tag>, ret: mir::BasicBlock, @@ -204,6 +211,7 @@ fn emulate_foreign_item_by_name( match link_name { // Miri-specific extern functions "miri_static_root" => { + check_abi(abi, Abi::Rust)?; let &[ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let ptr = this.force_ptr(ptr)?; @@ -215,23 +223,27 @@ fn emulate_foreign_item_by_name( // Obtains a Miri backtrace. See the README for details. "miri_get_backtrace" => { + check_abi(abi, Abi::Rust)?; this.handle_miri_get_backtrace(args, dest)?; } // Resolves a Miri backtrace frame. See the README for details. "miri_resolve_frame" => { + check_abi(abi, Abi::Rust)?; this.handle_miri_resolve_frame(args, dest)?; } // Standard C allocation "malloc" => { + check_abi(abi, Abi::C)?; 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" => { + check_abi(abi, Abi::C)?; 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)?; @@ -241,11 +253,13 @@ fn emulate_foreign_item_by_name( this.write_scalar(res, dest)?; } "free" => { + check_abi(abi, Abi::C)?; let &[ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; this.free(ptr, MiriMemoryKind::C)?; } "realloc" => { + check_abi(abi, Abi::C)?; let &[old_ptr, new_size] = check_arg_count(args)?; let old_ptr = this.read_scalar(old_ptr)?.check_init()?; let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?; @@ -257,6 +271,7 @@ fn emulate_foreign_item_by_name( // (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" => { + check_abi(abi, Abi::Rust)?; 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)?; @@ -269,6 +284,7 @@ fn emulate_foreign_item_by_name( this.write_scalar(ptr, dest)?; } "__rust_alloc_zeroed" => { + check_abi(abi, Abi::Rust)?; 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)?; @@ -283,6 +299,7 @@ fn emulate_foreign_item_by_name( this.write_scalar(ptr, dest)?; } "__rust_dealloc" => { + check_abi(abi, Abi::Rust)?; let &[ptr, old_size, align] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; @@ -296,6 +313,7 @@ fn emulate_foreign_item_by_name( )?; } "__rust_realloc" => { + check_abi(abi, Abi::Rust)?; let &[ptr, old_size, align, new_size] = check_arg_count(args)?; let ptr = this.force_ptr(this.read_scalar(ptr)?.check_init()?)?; let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?; @@ -316,6 +334,7 @@ fn emulate_foreign_item_by_name( // C memory handling functions "memcmp" => { + check_abi(abi, Abi::C)?; let &[left, right, n] = check_arg_count(args)?; let left = this.read_scalar(left)?.check_init()?; let right = this.read_scalar(right)?.check_init()?; @@ -336,6 +355,7 @@ fn emulate_foreign_item_by_name( this.write_scalar(Scalar::from_i32(result), dest)?; } "memrchr" => { + check_abi(abi, Abi::C)?; let &[ptr, val, num] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let val = this.read_scalar(val)?.to_i32()? as u8; @@ -354,6 +374,7 @@ fn emulate_foreign_item_by_name( } } "memchr" => { + check_abi(abi, Abi::C)?; let &[ptr, val, num] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let val = this.read_scalar(val)?.to_i32()? as u8; @@ -371,6 +392,7 @@ fn emulate_foreign_item_by_name( } } "strlen" => { + check_abi(abi, Abi::C)?; let &[ptr] = check_arg_count(args)?; let ptr = this.read_scalar(ptr)?.check_init()?; let n = this.memory.read_c_str(ptr)?.len(); @@ -386,6 +408,7 @@ fn emulate_foreign_item_by_name( | "asinf" | "atanf" => { + check_abi(abi, Abi::C)?; let &[f] = check_arg_count(args)?; // FIXME: Using host floats. let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); @@ -405,6 +428,7 @@ fn emulate_foreign_item_by_name( | "hypotf" | "atan2f" => { + check_abi(abi, Abi::C)?; 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) @@ -426,6 +450,7 @@ fn emulate_foreign_item_by_name( | "asin" | "atan" => { + check_abi(abi, Abi::C)?; let &[f] = check_arg_count(args)?; // FIXME: Using host floats. let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); @@ -445,6 +470,7 @@ fn emulate_foreign_item_by_name( | "hypot" | "atan2" => { + check_abi(abi, Abi::C)?; let &[f1, f2] = check_arg_count(args)?; // FIXME: Using host floats. let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?); @@ -460,6 +486,7 @@ fn emulate_foreign_item_by_name( | "ldexp" | "scalbn" => { + check_abi(abi, Abi::C)?; let &[x, exp] = check_arg_count(args)?; // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. let x = this.read_scalar(x)?.to_f64()?; @@ -481,10 +508,12 @@ fn emulate_foreign_item_by_name( // Architecture-specific shims "llvm.x86.sse2.pause" if this.tcx.sess.target.arch == "x86" || this.tcx.sess.target.arch == "x86_64" => { + check_abi(abi, Abi::C)?; let &[] = check_arg_count(args)?; this.yield_active_thread(); } "llvm.aarch64.hint" if this.tcx.sess.target.arch == "aarch64" => { + check_abi(abi, Abi::C)?; let &[hint] = check_arg_count(args)?; let hint = this.read_scalar(hint)?.to_i32()?; match hint { @@ -499,8 +528,8 @@ fn emulate_foreign_item_by_name( // Platform-specific shims _ => match this.tcx.sess.target.os.as_str() { - "linux" | "macos" => return shims::posix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret), - "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret), + "linux" | "macos" => return shims::posix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), + "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret), target => throw_unsup_format!("the target `{}` is not supported", target), } }; diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 90dcc4d8ff1..c2b8809efbb 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -16,6 +16,7 @@ use log::trace; use rustc_middle::{mir, ty}; +use rustc_target::spec::abi::Abi; use crate::*; use helpers::check_arg_count; @@ -25,6 +26,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn find_mir_or_eval_fn( &mut self, instance: ty::Instance<'tcx>, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, unwind: Option, @@ -48,7 +50,7 @@ fn find_mir_or_eval_fn( // to run extra MIR), and Ok(Some(body)) if we found MIR to run for the // foreign function // Any needed call to `goto_block` will be performed by `emulate_foreign_item`. - return this.emulate_foreign_item(instance.def_id(), args, ret, unwind); + return this.emulate_foreign_item(instance.def_id(), abi, args, ret, unwind); } // Otherwise, load the MIR. diff --git a/src/shims/posix/dlsym.rs b/src/shims/posix/dlsym.rs index 52d9844bed5..e05419f47e9 100644 --- a/src/shims/posix/dlsym.rs +++ b/src/shims/posix/dlsym.rs @@ -1,6 +1,8 @@ use rustc_middle::mir; +use rustc_target::spec::abi::Abi; use crate::*; +use helpers::check_abi; use shims::posix::linux::dlsym as linux; use shims::posix::macos::dlsym as macos; @@ -27,10 +29,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_dlsym( &mut self, dlsym: Dlsym, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); + check_abi(abi, Abi::C)?; match dlsym { Dlsym::Linux(dlsym) => linux::EvalContextExt::call_dlsym(this, dlsym, args, ret), Dlsym::MacOs(dlsym) => macos::EvalContextExt::call_dlsym(this, dlsym, args, ret), diff --git a/src/shims/posix/foreign_items.rs b/src/shims/posix/foreign_items.rs index f92242f56d9..e576c6f32e9 100644 --- a/src/shims/posix/foreign_items.rs +++ b/src/shims/posix/foreign_items.rs @@ -2,9 +2,10 @@ use rustc_middle::mir; use rustc_target::abi::{Align, LayoutOf, Size}; +use rustc_target::spec::abi::Abi; use crate::*; -use helpers::check_arg_count; +use helpers::{check_abi, check_arg_count}; use shims::posix::fs::EvalContextExt as _; use shims::posix::sync::EvalContextExt as _; use shims::posix::thread::EvalContextExt as _; @@ -14,12 +15,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn emulate_foreign_item_by_name( &mut self, link_name: &str, + abi: Abi, args: &[OpTy<'tcx, Tag>], dest: PlaceTy<'tcx, Tag>, ret: mir::BasicBlock, ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); + check_abi(abi, Abi::C)?; + match link_name { // Environment related shims "getenv" => { diff --git a/src/shims/windows/dlsym.rs b/src/shims/windows/dlsym.rs index c88a1646115..ce119d1090b 100644 --- a/src/shims/windows/dlsym.rs +++ b/src/shims/windows/dlsym.rs @@ -1,9 +1,10 @@ use rustc_middle::mir; +use rustc_target::spec::abi::Abi; use log::trace; use crate::*; -use helpers::check_arg_count; +use helpers::{check_abi, check_arg_count}; use shims::windows::sync::EvalContextExt as _; #[derive(Debug, Copy, Clone)] @@ -38,6 +39,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn call_dlsym( &mut self, dlsym: Dlsym, + abi: Abi, args: &[OpTy<'tcx, Tag>], ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>, ) -> InterpResult<'tcx> { @@ -45,6 +47,8 @@ fn call_dlsym( let (dest, ret) = ret.expect("we don't support any diverging dlsym"); assert!(this.tcx.sess.target.os == "windows"); + check_abi(abi, Abi::System)?; + match dlsym { Dlsym::AcquireSRWLockExclusive => { let &[ptr] = check_arg_count(args)?; diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 17d566d18ee..52b68b9f1bc 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -2,9 +2,10 @@ use rustc_middle::mir; use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; use crate::*; -use helpers::check_arg_count; +use helpers::{check_abi, check_arg_count}; use shims::windows::sync::EvalContextExt as _; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} @@ -12,12 +13,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx fn emulate_foreign_item_by_name( &mut self, link_name: &str, + abi: Abi, args: &[OpTy<'tcx, Tag>], dest: PlaceTy<'tcx, Tag>, _ret: mir::BasicBlock, ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); + check_abi(abi, Abi::System)?; + // Windows API stubs. // HANDLE = isize // DWORD = ULONG = u32