Skip to content

Allow suppressing unsupported FFI calls #1807

Closed
@landaire

Description

@landaire

In the project I'm working on we have many tests that we'd potentially like to run with Miri in our CI/CD. These tests may invoke syscalls or other unsupported FFI operations that we would like to suppress and treat as successful tests for a few reasons:

  • Reduce the developer burden of applying cfg(miri) to ignore specific tests.
  • Tests invoking unsupported foreign functions will still be emulated up until they perform the foreign function call. Testing to this point may still yield valuable results.
  • Opting out of specific tests requires conscious thought of whether a test should be re-enabled after a refactoring to remove a foreign function call that would otherwise raise an error.

I believe this could be handled with the introduction of a new -Z variable, -Zmiri-ignore-ffi-failure, that would simply treat these failures as an early exit with status code 0. Here's the proposed change to Miri:

Click to view diff
diff --git a/src/bin/miri.rs b/src/bin/miri.rs
index 4a1ea3a5..78f9b196 100644
--- a/src/bin/miri.rs
+++ b/src/bin/miri.rs
@@ -230,6 +230,9 @@ fn main() {
                 "-Zmiri-disable-isolation" => {
                     miri_config.communicate = true;
                 }
+                "-Zmiri-ignore-ffi-failures" => {
+                    miri_config.ignore_unsupported_ffi_failures = true;
+                }
                 "-Zmiri-ignore-leaks" => {
                     miri_config.ignore_leaks = true;
                 }
diff --git a/src/eval.rs b/src/eval.rs
index 1e46015f..8acc3020 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -35,6 +35,8 @@ pub struct MiriConfig {
     pub communicate: bool,
     /// Determines if memory leaks should be ignored.
     pub ignore_leaks: bool,
+    /// Determines if failures caused by unsupported FFI failures should be ignored.
+    pub ignore_unsupported_ffi_failures: bool,
     /// Environment variables that should always be isolated from the host.
     pub excluded_env_vars: Vec<String>,
     /// Command-line arguments passed to the interpreted program.
@@ -206,6 +208,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
 pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
     // Copy setting before we move `config`.
     let ignore_leaks = config.ignore_leaks;
+    let ignore_unsupported_ffi_failures= config.ignore_unsupported_ffi_failures;
 
     let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
         Ok(v) => v,
@@ -267,6 +270,14 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
             }
             Some(return_code)
         }
-        Err(e) => report_error(&ecx, e),
+        Err(e) => {
+            if let InterpError::Unsupported(UnsupportedOpInfo::UnsupportedForeignFunction(_name)) = e.kind() {
+                if ignore_unsupported_ffi_failures {
+                    // clean exit
+                    return Some(0);
+                }
+            }
+            report_error(&ecx, e)
+        }
     }
 }
diff --git a/src/shims/posix/linux/foreign_items.rs b/src/shims/posix/linux/foreign_items.rs
index f7d7706e..d78e6da3 100644
--- a/src/shims/posix/linux/foreign_items.rs
+++ b/src/shims/posix/linux/foreign_items.rs
@@ -214,7 +214,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
                 this.write_null(dest)?;
             }
 
-            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
+            _ => throw_unsup_foreign_function(link_name),
         };
 
         Ok(true)
diff --git a/src/shims/posix/macos/foreign_items.rs b/src/shims/posix/macos/foreign_items.rs
index 9a7d3be1..03656395 100644
--- a/src/shims/posix/macos/foreign_items.rs
+++ b/src/shims/posix/macos/foreign_items.rs
@@ -156,7 +156,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
                 this.write_scalar(addr, dest)?;
             }
 
-            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
+            _ => throw_unsup_foreign_function(link_name),
         };
 
         Ok(true)
diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs
index b246ccc3..e4211669 100644
--- a/src/shims/windows/foreign_items.rs
+++ b/src/shims/windows/foreign_items.rs
@@ -415,7 +415,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
                 this.write_scalar(Scalar::from_i32(1), dest)?;
             }
 
-            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
+            _ => throw_unsup_foreign_function(link_name),
         }
 
         Ok(true)

This would require changes to rustc_middle as well for a new macro, throw_unsup_foreign_function, and a new UnsupportedOpInfo variant, UnsupportedOpInfo::UnsupportedForeignFunction(String).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions