Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8fad3a1
tidy: allow stdlib to depend on moto-rt
lasiotus Sep 24, 2025
a828ffc
Add Motor OS std library port
lasiotus Oct 8, 2025
12c0d69
use check_nondet helper in a few more places
RalfJung Oct 13, 2025
42eb21c
Merge pull request #4629 from RalfJung/check-nondet
RalfJung Oct 13, 2025
fe91820
native-lib: support all types with Scalar layout
RalfJung Oct 14, 2025
2feb8bb
better error when native code tries to execute Rust function
RalfJung Oct 14, 2025
0317934
Merge pull request #4628 from RalfJung/native-lib
RalfJung Oct 14, 2025
75b60f1
Prepare for merging from rust-lang/rust
Oct 15, 2025
bcef7d5
Merge ref '235a4c083eb2' from rust-lang/rust
Oct 15, 2025
a9fc0ca
fmt
Oct 15, 2025
1819f5e
don't debug-print an Any
RalfJung Oct 15, 2025
afa2315
Merge pull request #4631 from rust-lang/rustup-2025-10-15
RalfJung Oct 15, 2025
a6705ea
Merge pull request #4632 from RalfJung/supervisor-panic
RalfJung Oct 15, 2025
7c419e8
native-lib: bump libffi
nia-e Oct 15, 2025
51cab7c
avoid some Vector allocations
RalfJung Oct 15, 2025
a084f45
Merge pull request #4634 from nia-e/new-libffi
RalfJung Oct 15, 2025
1de9b49
remove duplicate inline macro
henrichter Oct 15, 2025
0ecee8c
Prepare for merging from rust-lang/rust
RalfJung Oct 15, 2025
7c28d96
Merge ref '28d0a4a205f9' from rust-lang/rust
RalfJung Oct 15, 2025
984542c
Don't highlight `let` expressions as having type `bool`
pommicket Oct 15, 2025
952be8e
fix genmc build
RalfJung Oct 15, 2025
d458363
Merge pull request #4635 from RalfJung/rustup
RalfJung Oct 15, 2025
2f04473
update lockfile
RalfJung Oct 15, 2025
8787c0b
Use `bit_set::Word` in a couple more places.
nnethercote Oct 16, 2025
74ac3ec
style-guide: fix typo for empty struct advice
fee1-dead Oct 16, 2025
ea08a7d
CFI: Rewrite `FnPtrShim` when generalizing
rcvalle Aug 5, 2025
8cce703
Rollup merge of #144936 - rcvalle:rust-cfi-fix-144641, r=lcnr
matthiaskrgr Oct 16, 2025
67b511a
Rollup merge of #147000 - moturus:motor-os_stdlib_pr, r=tgross35
matthiaskrgr Oct 16, 2025
e954bd6
Rollup merge of #147732 - h3nryc0ding:master, r=chenyukang
matthiaskrgr Oct 16, 2025
28b216e
Rollup merge of #147738 - pommicket:issue-147665, r=madsmtm
matthiaskrgr Oct 16, 2025
838a18c
Rollup merge of #147744 - RalfJung:miri, r=RalfJung
matthiaskrgr Oct 16, 2025
d48b674
Rollup merge of #147751 - nnethercote:bit_set-Word, r=Zalathar
matthiaskrgr Oct 16, 2025
77afaa5
Rollup merge of #147752 - fee1-dead-contrib:sgtypo, r=chenyukang
matthiaskrgr Oct 16, 2025
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
13 changes: 13 additions & 0 deletions src/tools/miri/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub enum NonHaltingDiagnostic {
NativeCallSharedMem {
tracing: bool,
},
NativeCallFnPtr,
WeakMemoryOutdatedLoad {
ptr: Pointer,
},
Expand Down Expand Up @@ -644,6 +645,11 @@ impl<'tcx> MiriMachine<'tcx> {
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
NativeCallSharedMem { .. } =>
("sharing memory with a native function".to_string(), DiagLevel::Warning),
NativeCallFnPtr =>
(
"sharing a function pointer with a native function".to_string(),
DiagLevel::Warning,
),
ExternTypeReborrow =>
("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } =>
Expand Down Expand Up @@ -682,6 +688,8 @@ impl<'tcx> MiriMachine<'tcx> {
Int2Ptr { .. } => format!("integer-to-pointer cast"),
NativeCallSharedMem { .. } =>
format!("sharing memory with a native function called via FFI"),
NativeCallFnPtr =>
format!("sharing a function pointer with a native function called via FFI"),
WeakMemoryOutdatedLoad { ptr } =>
format!("weak memory emulation: outdated value returned from load at {ptr}"),
ExternTypeReborrow =>
Expand Down Expand Up @@ -779,6 +787,11 @@ impl<'tcx> MiriMachine<'tcx> {
),
]
},
NativeCallFnPtr => {
vec![note!(
"calling Rust functions from C is not supported and will, in the best case, crash the program"
)]
}
ExternTypeReborrow => {
assert!(self.borrow_tracker.as_ref().is_some_and(|b| {
matches!(
Expand Down
65 changes: 41 additions & 24 deletions src/tools/miri/src/shims/native_lib/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::sync::atomic::AtomicBool;
use libffi::low::CodePtr;
use libffi::middle::Type as FfiType;
use rustc_abi::{HasDataLayout, Size};
use rustc_middle::ty::layout::HasTypingEnv;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, IntTy, Ty, UintTy};
use rustc_span::Symbol;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -277,7 +277,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {

// This should go first so that we emit unsupported before doing a bunch
// of extra work for types that aren't supported yet.
let ty = this.ty_to_ffitype(v.layout.ty)?;
let ty = this.ty_to_ffitype(v.layout)?;

// Helper to print a warning when a pointer is shared with the native code.
let expose = |prov: Provenance| -> InterpResult<'tcx> {
Expand Down Expand Up @@ -386,34 +386,44 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_ref();
let mut fields = vec![];
for field in &adt_def.non_enum_variant().fields {
fields.push(this.ty_to_ffitype(field.ty(*this.tcx, args))?);
let layout = this.layout_of(field.ty(*this.tcx, args))?;
fields.push(this.ty_to_ffitype(layout)?);
}

interp_ok(FfiType::structure(fields))
}

/// Gets the matching libffi type for a given Ty.
fn ty_to_ffitype(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, FfiType> {
let this = self.eval_context_ref();
interp_ok(match ty.kind() {
ty::Int(IntTy::I8) => FfiType::i8(),
ty::Int(IntTy::I16) => FfiType::i16(),
ty::Int(IntTy::I32) => FfiType::i32(),
ty::Int(IntTy::I64) => FfiType::i64(),
ty::Int(IntTy::Isize) => FfiType::isize(),
ty::Uint(UintTy::U8) => FfiType::u8(),
ty::Uint(UintTy::U16) => FfiType::u16(),
ty::Uint(UintTy::U32) => FfiType::u32(),
ty::Uint(UintTy::U64) => FfiType::u64(),
ty::Uint(UintTy::Usize) => FfiType::usize(),
ty::RawPtr(pointee_ty, _mut) => {
if !pointee_ty.is_sized(*this.tcx, this.typing_env()) {
throw_unsup_format!("passing a pointer to an unsized type over FFI: {}", ty);
}
FfiType::pointer()
}
ty::Adt(adt_def, args) => self.adt_to_ffitype(ty, *adt_def, args)?,
_ => throw_unsup_format!("unsupported argument type for native call: {}", ty),
fn ty_to_ffitype(&self, layout: TyAndLayout<'tcx>) -> InterpResult<'tcx, FfiType> {
use rustc_abi::{AddressSpace, BackendRepr, Integer, Primitive};

// `BackendRepr::Scalar` is also a signal to pass this type as a scalar in the ABI. This
// matches what codegen does. This does mean that we support some types whose ABI is not
// stable, but that's fine -- we are anyway quite conservative in native-lib mode.
if let BackendRepr::Scalar(s) = layout.backend_repr {
// Simple sanity-check: this cannot be `repr(C)`.
assert!(!layout.ty.ty_adt_def().is_some_and(|adt| adt.repr().c()));
return interp_ok(match s.primitive() {
Primitive::Int(Integer::I8, /* signed */ true) => FfiType::i8(),
Primitive::Int(Integer::I16, /* signed */ true) => FfiType::i16(),
Primitive::Int(Integer::I32, /* signed */ true) => FfiType::i32(),
Primitive::Int(Integer::I64, /* signed */ true) => FfiType::i64(),
Primitive::Int(Integer::I8, /* signed */ false) => FfiType::u8(),
Primitive::Int(Integer::I16, /* signed */ false) => FfiType::u16(),
Primitive::Int(Integer::I32, /* signed */ false) => FfiType::u32(),
Primitive::Int(Integer::I64, /* signed */ false) => FfiType::u64(),
Primitive::Pointer(AddressSpace::ZERO) => FfiType::pointer(),
_ =>
throw_unsup_format!(
"unsupported scalar argument type for native call: {}",
layout.ty
),
});
}
interp_ok(match layout.ty.kind() {
// Scalar types have already been handled above.
ty::Adt(adt_def, args) => self.adt_to_ffitype(layout.ty, *adt_def, args)?,
_ => throw_unsup_format!("unsupported argument type for native call: {}", layout.ty),
})
}
}
Expand Down Expand Up @@ -454,6 +464,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// pointer was passed as argument). Uninitialised memory is left as-is, but any data
// exposed this way is garbage anyway.
this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| {
if matches!(info.kind, AllocKind::Function) {
static DEDUP: AtomicBool = AtomicBool::new(false);
if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) {
// Newly set, so first time we get here.
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallFnPtr);
}
}
// If there is no data behind this pointer, skip this.
if !matches!(info.kind, AllocKind::LiveData) {
return interp_ok(());
Expand Down
13 changes: 12 additions & 1 deletion src/tools/miri/src/shims/native_lib/trace/parent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,18 @@ fn handle_segfault(
// Don't use wait_for_signal here since 1 instruction doesn't give room
// for any uncertainty + we don't want it `cont()`ing randomly by accident
// Also, don't let it continue with unprotected memory if something errors!
let _ = wait::waitid(wait::Id::Pid(pid), WAIT_FLAGS).map_err(|_| ExecEnd(None))?;
let stat = wait::waitid(wait::Id::Pid(pid), WAIT_FLAGS).map_err(|_| ExecEnd(None))?;
match stat {
wait::WaitStatus::Signaled(_, s, _)
| wait::WaitStatus::Stopped(_, s)
| wait::WaitStatus::PtraceEvent(_, s, _) =>
assert!(
!matches!(s, signal::SIGSEGV),
"native code segfaulted when re-trying memory access\n\
is the native code trying to call a Rust function?"
),
_ => (),
}

// Zero out again to be safe
for a in (ch_stack..ch_stack.strict_add(CALLBACK_STACK_SIZE)).step_by(ARCH_WORD_SIZE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ note: inside `main`
LL | test_access_pointer();
| ^^^^^^^^^^^^^^^^^^^^^

warning: sharing a function pointer with a native function called via FFI
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
LL | pass_fn_ptr(Some(nop)); // this one is not
| ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function
|
= help: calling Rust functions from C is not supported and will, in the best case, crash the program
= note: BACKTRACE:
= note: inside `pass_fn_ptr` at tests/native-lib/pass/ptr_read_access.rs:LL:CC
note: inside `main`
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
LL | pass_fn_ptr();
| ^^^^^^^^^^^^^

20 changes: 20 additions & 0 deletions src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
//@[trace] compile-flags: -Zmiri-native-lib-enable-tracing
//@compile-flags: -Zmiri-permissive-provenance

use std::ptr::NonNull;

fn main() {
test_access_pointer();
test_access_simple();
test_access_nested();
test_access_static();
pass_fn_ptr();
}

/// Test function that dereferences an int pointer and prints its contents from C.
Expand All @@ -30,11 +33,15 @@ fn test_access_simple() {

extern "C" {
fn access_simple(s_ptr: *const Simple) -> i32;
fn access_simple2(s_ptr: NonNull<Simple>) -> i32;
fn access_simple3(s_ptr: Option<NonNull<Simple>>) -> i32;
}

let simple = Simple { field: -42 };

assert_eq!(unsafe { access_simple(&simple) }, -42);
assert_eq!(unsafe { access_simple2(NonNull::from(&simple)) }, -42);
assert_eq!(unsafe { access_simple3(Some(NonNull::from(&simple))) }, -42);
}

/// Test function that dereferences nested struct pointers and accesses fields.
Expand Down Expand Up @@ -75,3 +82,16 @@ fn test_access_static() {

assert_eq!(unsafe { access_static(&STATIC) }, 9001);
}

fn pass_fn_ptr() {
extern "C" {
fn pass_fn_ptr(s: Option<extern "C" fn()>);
}

extern "C" fn nop() {}

unsafe {
pass_fn_ptr(None); // this one is fine
pass_fn_ptr(Some(nop)); // this one is not
}
}
15 changes: 15 additions & 0 deletions src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,18 @@ note: inside `main`
LL | test_access_pointer();
| ^^^^^^^^^^^^^^^^^^^^^

warning: sharing a function pointer with a native function called via FFI
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
LL | pass_fn_ptr(Some(nop)); // this one is not
| ^^^^^^^^^^^^^^^^^^^^^^ sharing a function pointer with a native function
|
= help: calling Rust functions from C is not supported and will, in the best case, crash the program
= note: BACKTRACE:
= note: inside `pass_fn_ptr` at tests/native-lib/pass/ptr_read_access.rs:LL:CC
note: inside `main`
--> tests/native-lib/pass/ptr_read_access.rs:LL:CC
|
LL | pass_fn_ptr();
| ^^^^^^^^^^^^^

13 changes: 13 additions & 0 deletions src/tools/miri/tests/native-lib/ptr_read_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ typedef struct Simple {
EXPORT int32_t access_simple(const Simple *s_ptr) {
return s_ptr->field;
}
// Some copies so Rust can import them at different types.
EXPORT int32_t access_simple2(const Simple *s_ptr) {
return s_ptr->field;
}
EXPORT int32_t access_simple3(const Simple *s_ptr) {
return s_ptr->field;
}

/* Test: test_access_nested */

Expand Down Expand Up @@ -55,3 +62,9 @@ EXPORT int32_t access_static(const Static *s_ptr) {
EXPORT uintptr_t do_one_deref(const int32_t ***ptr) {
return (uintptr_t)*ptr;
}

/* Test: pass_fn_ptr */

EXPORT void pass_fn_ptr(void f(void)) {
(void)f; // suppress unused warning
}