Skip to content

Commit 17ab519

Browse files
committed
Additional improvements
This commit brings better error reporting and some additional cleanups.
1 parent a464170 commit 17ab519

File tree

5 files changed

+143
-90
lines changed

5 files changed

+143
-90
lines changed

src/alloc_addresses/mod.rs

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use std::cell::RefCell;
88

99
use rustc_abi::{Align, Size};
1010
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
11-
use rustc_middle::ty::{InstanceKind, TyCtxt};
11+
use rustc_middle::ty::TyCtxt;
1212

1313
pub use self::address_generator::AddressGenerator;
1414
use self::reuse_pool::ReusePool;
15+
use crate::alloc::MiriAllocParams;
1516
use crate::concurrency::VClock;
1617
use crate::diagnostics::SpanDedupDiagnostic;
1718
use crate::*;
@@ -162,48 +163,41 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
162163
this.get_alloc_bytes_unchecked_raw(alloc_id)?
163164
}
164165
}
165-
AllocKind::Function | AllocKind::VTable => {
166-
// Allocate some dummy memory to get a unique address for this function/vtable.
167-
let alloc_bytes = MiriAllocBytes::from_bytes(
168-
&[0u8; 1],
169-
Align::from_bytes(1).unwrap(),
170-
params,
171-
);
172-
let mut ptr = alloc_bytes.as_ptr();
173-
// Leak the underlying memory to ensure it remains unique.
174-
std::mem::forget(alloc_bytes);
166+
#[cfg(all(unix, feature = "native-lib"))]
167+
AllocKind::Function => {
175168
if let Some(GlobalAlloc::Function { instance, .. }) =
176169
this.tcx.try_get_global_alloc(alloc_id)
170+
&& let rustc_middle::ty::InstanceKind::Item(def_id) = instance.def
171+
&& let Some(closure) = crate::shims::native_lib::build_libffi_closure(
172+
this,
173+
this.tcx.fn_sig(def_id).skip_binder().skip_binder(),
174+
)
177175
{
178-
#[cfg(all(unix, feature = "native-lib"))]
179-
if let InstanceKind::Item(def_id) = instance.def {
180-
let sig = this.tcx.fn_sig(def_id).skip_binder().skip_binder();
181-
let closure =
182-
crate::shims::native_lib::build_libffi_closure(this, sig)?;
183-
if let Some(closure) = closure {
184-
let closure = Box::leak(Box::new(closure));
185-
// Libffi returns a **reference** to a function ptr here
186-
// (The actual argument type doesn't matter)
187-
let fn_ptr = unsafe {
188-
closure.instantiate_code_ptr::<unsafe extern "C" fn(*const std::ffi::c_void)>()
189-
};
190-
// Therefore we need to dereference the reference to get the actual function pointer
191-
let fn_ptr = *fn_ptr;
192-
#[expect(
193-
clippy::as_conversions,
194-
reason = "No better way to cast a function ptr to a ptr"
195-
)]
196-
{
197-
// After that we need to cast the function pointer to the
198-
// expected pointer type. At this point we don't actually care about the
199-
// type of this pointer
200-
ptr = (fn_ptr as *const std::ffi::c_void).cast();
201-
}
202-
}
176+
let closure = Box::leak(Box::new(closure));
177+
// Libffi returns a **reference** to a function ptr here
178+
// (The actual argument type doesn't matter)
179+
let fn_ptr = unsafe {
180+
closure.instantiate_code_ptr::<unsafe extern "C" fn(*const std::ffi::c_void)>()
181+
};
182+
// Therefore we need to dereference the reference to get the actual function pointer
183+
let fn_ptr = *fn_ptr;
184+
#[expect(
185+
clippy::as_conversions,
186+
reason = "No better way to cast a function ptr to a ptr"
187+
)]
188+
{
189+
// After that we need to cast the function pointer to the
190+
// expected pointer type. At this point we don't actually care about the
191+
// type of this pointer
192+
(fn_ptr as *const std::ffi::c_void).cast()
203193
}
194+
} else {
195+
dummy_alloc(params)
204196
}
205-
ptr
206197
}
198+
#[cfg(not(all(unix, feature = "native-lib")))]
199+
AllocKind::Function => dummy_alloc(params),
200+
AllocKind::VTable => dummy_alloc(params),
207201
AllocKind::TypeId | AllocKind::Dead => unreachable!(),
208202
};
209203
// We don't have to expose this pointer yet, we do that in `prepare_for_native_call`.
@@ -235,6 +229,15 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
235229
}
236230
}
237231

232+
fn dummy_alloc(params: MiriAllocParams) -> *const u8 {
233+
// Allocate some dummy memory to get a unique address for this function/vtable.
234+
let alloc_bytes = MiriAllocBytes::from_bytes(&[0u8; 1], Align::from_bytes(1).unwrap(), params);
235+
let ptr = alloc_bytes.as_ptr();
236+
// Leak the underlying memory to ensure it remains unique.
237+
std::mem::forget(alloc_bytes);
238+
ptr
239+
}
240+
238241
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
239242
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
240243
// Returns the `AllocId` that corresponds to the specified addr,

src/shims/native_lib/mod.rs

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Implements calling functions from a native library.
22
3+
use std::cell::Cell;
34
use std::ops::Deref;
45
use std::os::raw::c_void;
6+
use std::ptr::NonNull;
57
use std::sync::atomic::AtomicBool;
68

79
use libffi::low::CodePtr;
@@ -17,6 +19,10 @@ use self::helpers::ToSoft;
1719

1820
mod ffi;
1921

22+
thread_local! {
23+
static INTERP_CTX: Cell<Option<NonNull<c_void>>> = const { Cell::new(None) };
24+
}
25+
2026
#[cfg_attr(
2127
not(all(
2228
target_os = "linux",
@@ -28,6 +34,7 @@ mod ffi;
2834
pub mod trace;
2935

3036
use self::ffi::OwnedArg;
37+
use crate::diagnostics::DiagLevel;
3138
use crate::*;
3239

3340
/// The final results of an FFI trace, containing every relevant event detected
@@ -95,6 +102,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
95102
trace::Supervisor::do_ffi(alloc, || {
96103
// Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
97104
// as the specified primitive integer type
105+
INTERP_CTX.set(NonNull::new(
106+
(this as *const MiriInterpCx<'tcx> as *mut MiriInterpCx<'tcx>).cast(),
107+
));
98108
let scalar = match dest.layout.ty.kind() {
99109
// ints
100110
ty::Int(IntTy::I8) => {
@@ -455,21 +465,21 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
455465
pub fn build_libffi_closure<'tcx, 'this>(
456466
this: &'this MiriInterpCx<'tcx>,
457467
fn_ptr: rustc_middle::ty::FnSig<'tcx>,
458-
) -> InterpResult<'tcx, Option<libffi::middle::Closure<'this>>> {
468+
) -> Option<libffi::middle::Closure<'this>> {
459469
let mut args = Vec::new();
460470
for input in fn_ptr.inputs().iter() {
461471
let layout = match this.layout_of(*input) {
462472
Ok(layout) => layout,
463473
Err(e) => {
464474
tracing::info!(?e, "Skip closure");
465-
return interp_ok(None);
475+
return None;
466476
}
467477
};
468478
let ty = match this.ty_to_ffitype(layout).report_err() {
469479
Ok(ty) => ty,
470480
Err(e) => {
471481
tracing::info!(?e, "Skip closure");
472-
return interp_ok(None);
482+
return None;
473483
}
474484
};
475485
args.push(ty);
@@ -480,27 +490,22 @@ pub fn build_libffi_closure<'tcx, 'this>(
480490
Ok(layout) => layout,
481491
Err(e) => {
482492
tracing::info!(?e, "Skip closure");
483-
return interp_ok(None);
493+
return None;
484494
}
485495
};
486496
match this.ty_to_ffitype(layout).report_err() {
487497
Ok(ty) => ty,
488498
Err(e) => {
489499
tracing::info!(?e, "Skip closure");
490-
return interp_ok(None);
500+
return None;
491501
}
492502
}
493503
};
494504
let closure_builder = libffi::middle::Builder::new().args(args).res(res_type);
495-
let data = CallbackData {
496-
args: fn_ptr.inputs().to_vec(),
497-
result: fn_ptr.output(),
498-
this,
499-
//link_name: *link_name,
500-
};
505+
let data = CallbackData {};
501506
let data = Box::leak(Box::new(data));
502507
let closure = closure_builder.into_closure(callback_callback, data);
503-
interp_ok(Some(closure))
508+
Some(closure)
504509
}
505510

506511
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -596,44 +601,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
596601
}
597602
}
598603

599-
struct CallbackData<'a, 'tcx> {
600-
args: Vec<Ty<'tcx>>,
601-
#[expect(dead_code, reason = "It's there for later")]
602-
result: Ty<'tcx>,
603-
this: &'a MiriInterpCx<'tcx>,
604-
}
604+
struct CallbackData {}
605605

606606
unsafe extern "C" fn callback_callback(
607-
cif: &libffi::low::ffi_cif,
607+
_cif: &libffi::low::ffi_cif,
608608
_result: &mut c_void,
609-
args: *const *const c_void,
610-
infos: &CallbackData<'_, '_>,
609+
_args: *const *const c_void,
610+
_infos: &CallbackData,
611611
) {
612-
debug_assert_eq!(cif.nargs.try_into(), Ok(infos.args.len()));
613-
let mut rust_args = Vec::with_capacity(infos.args.len());
614-
// We cast away the pointer to pointer to get a pointer to the actual argument
615-
let mut args = args.cast::<c_void>();
616-
for arg in &infos.args {
617-
let scalar = match arg.kind() {
618-
ty::RawPtr(..) => {
619-
let ptr = StrictPointer::new(Provenance::Wildcard, Size::from_bytes(args.addr()));
620-
// This offset moves the pointer to the next argument
621-
args = unsafe { args.offset(1) };
622-
Scalar::from_pointer(ptr, infos.this)
623-
}
624-
// the other types
625-
_ => todo!(),
626-
};
627-
rust_args.push(scalar);
612+
if let Some(this) = INTERP_CTX.get() {
613+
let ctx = unsafe { this.cast::<MiriInterpCx<'_>>().as_ref() };
614+
let stacktrace = ctx.generate_stacktrace();
615+
crate::diagnostics::report_msg(
616+
DiagLevel::Error,
617+
"tried to call a function pointer through the FFI boundary".into(),
618+
vec!["this function called a function pointer calling back into Rust".into()],
619+
vec![(None, "this is not supported yet by miri".into())],
620+
Vec::new(),
621+
&stacktrace,
622+
None,
623+
&ctx.machine,
624+
);
625+
} else {
626+
// There is no interpctx set which likely means we are running in a different thread
627+
// than that one that called into the native library. At this point we fall back
628+
// to just reporting an error via eprintln before aborting.
629+
eprintln!(
630+
"Tried to call a function pointer via FFI boundary. \
631+
That's not supported yet by miri"
632+
);
628633
}
629-
630634
// We abort the execution at this point as we cannot return the
631635
// expected value here.
632-
eprintln!(
633-
"Tried to call a function pointer via FFI boundary. \
634-
That's not supported yet by miri\nThis function pointer was registered by a call to `{}` \
635-
using an argument of the type `{}`",
636-
"todo: fill in name", "todo: fill in type"
637-
);
638636
std::process::exit(1);
639637
}

tests/native-lib/fail/call_function_ptr.notrace.stderr

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
warning: sharing memory with a native function called via FFI
22
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
33
|
4-
LL | call_fn_ptr(Some(nop)); // this one is not
4+
LL | call_fn_ptr(Some(nop));
55
| ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
66
|
77
= help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
@@ -19,7 +19,7 @@ LL | pass_fn_ptr()
1919
warning: sharing a function pointer with a native function called via FFI
2020
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
2121
|
22-
LL | call_fn_ptr(Some(nop)); // this one is not
22+
LL | call_fn_ptr(Some(nop));
2323
| ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function
2424
|
2525
= help: calling Rust functions from C is not supported and will, in the best case, crash the program
@@ -31,5 +31,31 @@ note: inside `main`
3131
LL | pass_fn_ptr()
3232
| ^^^^^^^^^^^^^
3333

34-
Tried to call a function pointer via FFI boundary. That's not supported yet by miri
35-
This function pointer was registered by a call to `todo: fill in name` using an argument of the type `todo: fill in type`
34+
error: tried to call a function pointer through the FFI boundary
35+
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
36+
|
37+
LL | call_fn_ptr(Some(nop));
38+
| ^^^^^^^^^^^^^^^^^^^^^^ this function called a function pointer calling back into Rust
39+
|
40+
= note: this is not supported yet by miri
41+
= note: BACKTRACE:
42+
= note: inside `pass_fn_ptr` at tests/native-lib/fail/call_function_ptr.rs:LL:CC
43+
note: inside `main`
44+
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
45+
|
46+
LL | pass_fn_ptr()
47+
| ^^^^^^^^^^^^^
48+
= note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at RUSTLIB/core/src/ops/function.rs:LL:CC
49+
= note: inside `std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
50+
= note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC
51+
= note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at RUSTLIB/core/src/ops/function.rs:LL:CC
52+
= note: inside `std::panicking::catch_unwind::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panicking.rs:LL:CC
53+
= note: inside `std::panicking::catch_unwind::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC
54+
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC
55+
= note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC
56+
= note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC
57+
= note: inside `std::panicking::catch_unwind::<isize, {closure@std::rt::lang_start_internal::{closure#0}}>` at RUSTLIB/std/src/panicking.rs:LL:CC
58+
= note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC
59+
= note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC
60+
= note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC
61+

tests/native-lib/fail/call_function_ptr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ fn pass_fn_ptr() {
1616

1717
unsafe {
1818
call_fn_ptr(None); // this one is fine
19-
call_fn_ptr(Some(nop)); // this one is not
19+
call_fn_ptr(Some(nop)); //~ ERROR: tried to call a function pointer through the FFI boundary
2020
}
2121
}

tests/native-lib/fail/call_function_ptr.trace.stderr

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
warning: sharing memory with a native function called via FFI
22
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
33
|
4-
LL | call_fn_ptr(Some(nop)); // this one is not
4+
LL | call_fn_ptr(Some(nop));
55
| ^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
66
|
77
= help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis
@@ -20,7 +20,7 @@ LL | pass_fn_ptr()
2020
warning: sharing a function pointer with a native function called via FFI
2121
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
2222
|
23-
LL | call_fn_ptr(Some(nop)); // this one is not
23+
LL | call_fn_ptr(Some(nop));
2424
| ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function
2525
|
2626
= help: calling Rust functions from C is not supported and will, in the best case, crash the program
@@ -32,5 +32,31 @@ note: inside `main`
3232
LL | pass_fn_ptr()
3333
| ^^^^^^^^^^^^^
3434

35-
Tried to call a function pointer via FFI boundary. That's not supported yet by miri
36-
This function pointer was registered by a call to `todo: fill in name` using an argument of the type `todo: fill in type`
35+
error: tried to call a function pointer through the FFI boundary
36+
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
37+
|
38+
LL | call_fn_ptr(Some(nop));
39+
| ^^^^^^^^^^^^^^^^^^^^^^ this function called a function pointer calling back into Rust
40+
|
41+
= note: this is not supported yet by miri
42+
= note: BACKTRACE:
43+
= note: inside `pass_fn_ptr` at tests/native-lib/fail/call_function_ptr.rs:LL:CC
44+
note: inside `main`
45+
--> tests/native-lib/fail/call_function_ptr.rs:LL:CC
46+
|
47+
LL | pass_fn_ptr()
48+
| ^^^^^^^^^^^^^
49+
= note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at RUSTLIB/core/src/ops/function.rs:LL:CC
50+
= note: inside `std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
51+
= note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC
52+
= note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at RUSTLIB/core/src/ops/function.rs:LL:CC
53+
= note: inside `std::panicking::catch_unwind::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panicking.rs:LL:CC
54+
= note: inside `std::panicking::catch_unwind::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC
55+
= note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC
56+
= note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC
57+
= note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC
58+
= note: inside `std::panicking::catch_unwind::<isize, {closure@std::rt::lang_start_internal::{closure#0}}>` at RUSTLIB/std/src/panicking.rs:LL:CC
59+
= note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC
60+
= note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC
61+
= note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC
62+

0 commit comments

Comments
 (0)