Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ABI check for shims #1670

Merged
merged 1 commit into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_middle::mir;
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;

Expand Down Expand Up @@ -553,6 +554,15 @@ pub fn check_arg_count<'a, 'tcx, const N: usize>(args: &'a [OpTy<'tcx, Tag>]) ->
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",
Expand Down
8 changes: 4 additions & 4 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,24 +353,24 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
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<mir::BasicBlock>,
) -> 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<mir::BasicBlock>,
) -> InterpResult<'tcx> {
ecx.call_dlsym(fn_val, args, ret)
ecx.call_dlsym(fn_val, abi, args, ret)
}

#[inline(always)]
Expand Down
6 changes: 4 additions & 2 deletions src/shims/dlsym.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_middle::mir;
use rustc_target::spec::abi::Abi;

use crate::*;
use shims::posix::dlsym as posix;
Expand Down Expand Up @@ -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),
}
}
}
39 changes: 34 additions & 5 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use log::trace;

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> {
Expand Down Expand Up @@ -112,6 +112,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
fn emulate_foreign_item(
&mut self,
def_id: DefId,
abi: Abi,
args: &[OpTy<'tcx, Tag>],
ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
Expand All @@ -130,25 +131,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?));
}
| "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),
Expand All @@ -165,6 +170,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// Normally, this will be either `libpanic_unwind` or `libpanic_abort`, but it could
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
// 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() {
Expand All @@ -179,7 +185,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}

// 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);
}
Expand All @@ -193,6 +199,7 @@ 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,
Expand All @@ -204,6 +211,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?;
Expand All @@ -215,23 +223,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

// 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)?;
Expand All @@ -241,11 +253,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?;
Expand All @@ -257,6 +271,7 @@ 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" => {
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)?;
Expand All @@ -269,6 +284,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?;
Expand All @@ -283,6 +299,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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)?;
Expand All @@ -296,6 +313,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
)?;
}
"__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)?;
Expand All @@ -316,6 +334,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

// 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()?;
Expand All @@ -336,6 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
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;
Expand All @@ -354,6 +374,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
}
"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;
Expand All @@ -371,6 +392,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
}
"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();
Expand All @@ -386,6 +408,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
| "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()?);
Expand All @@ -405,6 +428,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
| "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)
Expand All @@ -426,6 +450,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
| "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()?);
Expand All @@ -445,6 +470,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
| "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()?);
Expand All @@ -460,6 +486,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
| "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()?;
Expand All @@ -481,10 +508,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

// 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)?;
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
let &[] = check_arg_count(args)?;
this.yield_active_thread();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added another architecture-specific function here; can you rebase and also add the check_abi here?

}
"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 {
Expand All @@ -499,8 +528,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx

// 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),
}
};
Expand Down
4 changes: 3 additions & 1 deletion src/shims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod tls;
use log::trace;

use rustc_middle::{mir, ty};
use rustc_target::spec::abi::Abi;

use crate::*;
use helpers::check_arg_count;
Expand All @@ -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<mir::BasicBlock>,
Expand All @@ -48,7 +50,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// 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.
Expand Down
4 changes: 4 additions & 0 deletions src/shims/posix/dlsym.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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),
Expand Down
Loading