From b5fde0dae0af1a4a273830e695dc0aaf7c3885ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 16 Jul 2023 00:23:17 +0200 Subject: [PATCH 1/3] miri: fail when calling a function that requires an unavailable target feature miri will report an UB when calling a function that has a `#[target_feature(enable = ...)]` attribute is called and the required feature is not available. "Available features" are the same that `is_x86_feature_detected!` (or equivalent) reports to be available during miri execution (which can be enabled or disabled with the `-C target-feature` flag). --- compiler/rustc_const_eval/messages.ftl | 3 +++ .../src/interpret/terminator.rs | 19 +++++++++++++++++++ .../fail/function_calls/target_feature.rs | 11 +++++++++++ .../fail/function_calls/target_feature.stderr | 15 +++++++++++++++ .../pass/function_calls/target_feature.rs | 12 ++++++++++++ 5 files changed, 60 insertions(+) create mode 100644 src/tools/miri/tests/fail/function_calls/target_feature.rs create mode 100644 src/tools/miri/tests/fail/function_calls/target_feature.stderr create mode 100644 src/tools/miri/tests/pass/function_calls/target_feature.rs diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index e99005316b3fd..d8eade5bd2a0e 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -399,6 +399,9 @@ const_eval_unallowed_mutable_refs_raw = const_eval_unallowed_op_in_const_context = {$msg} +const_eval_unavailable_target_features_for_fn = + calling a function that requires unavailable target features: {$unavailable_feats} + const_eval_undefined_behavior = it is undefined behavior to use this value diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index c944782b487de..b6eea191aa67c 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -503,6 +503,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + let missing_features: Vec<_> = attrs + .target_features + .iter() + .copied() + .filter(|feature| !self.tcx.sess.target_features.contains(feature)) + .collect(); + if !missing_features.is_empty() { + let mut missing_features_str = String::from(missing_features[0].as_str()); + for missing_feature in missing_features[1..].iter() { + missing_features_str.push(','); + missing_features_str.push_str(missing_feature.as_str()); + } + throw_ub_custom!( + fluent::const_eval_unavailable_target_features_for_fn, + unavailable_feats = missing_features_str, + ); + } + if !callee_fn_abi.can_unwind { // The callee cannot unwind, so force the `Unreachable` unwind handling. unwind = mir::UnwindAction::Unreachable; diff --git a/src/tools/miri/tests/fail/function_calls/target_feature.rs b/src/tools/miri/tests/fail/function_calls/target_feature.rs new file mode 100644 index 0000000000000..be95241ea6781 --- /dev/null +++ b/src/tools/miri/tests/fail/function_calls/target_feature.rs @@ -0,0 +1,11 @@ +//@only-target-x86_64: uses x86 target features + +fn main() { + assert!(!is_x86_feature_detected!("ssse3")); + unsafe { + ssse3_fn(); //~ ERROR: calling a function that requires unavailable target features: ssse3 + } +} + +#[target_feature(enable = "ssse3")] +unsafe fn ssse3_fn() {} diff --git a/src/tools/miri/tests/fail/function_calls/target_feature.stderr b/src/tools/miri/tests/fail/function_calls/target_feature.stderr new file mode 100644 index 0000000000000..bddc6e7e89bdb --- /dev/null +++ b/src/tools/miri/tests/fail/function_calls/target_feature.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: calling a function that requires unavailable target features: ssse3 + --> $DIR/target_feature.rs:LL:CC + | +LL | ssse3_fn(); + | ^^^^^^^^^^ calling a function that requires unavailable target features: ssse3 + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/target_feature.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/pass/function_calls/target_feature.rs b/src/tools/miri/tests/pass/function_calls/target_feature.rs new file mode 100644 index 0000000000000..0be86ba373165 --- /dev/null +++ b/src/tools/miri/tests/pass/function_calls/target_feature.rs @@ -0,0 +1,12 @@ +//@only-target-x86_64: uses x86 target features +//@compile-flags: -C target-feature=+ssse3 + +fn main() { + assert!(is_x86_feature_detected!("ssse3")); + unsafe { + ssse3_fn(); + } +} + +#[target_feature(enable = "ssse3")] +unsafe fn ssse3_fn() {} From 3e8ce42d42ce6c6fed3f3db7ee77591a87eddc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 16 Jul 2023 13:20:06 +0200 Subject: [PATCH 2/3] Add const-eval test for `#[target_feature(enable = ...)]` function calls --- .../const-eval/const_fn_target_feature.rs | 17 +++++++++++++++++ .../const-eval/const_fn_target_feature.stderr | 9 +++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/ui/consts/const-eval/const_fn_target_feature.rs create mode 100644 tests/ui/consts/const-eval/const_fn_target_feature.stderr diff --git a/tests/ui/consts/const-eval/const_fn_target_feature.rs b/tests/ui/consts/const-eval/const_fn_target_feature.rs new file mode 100644 index 0000000000000..5d02ce3f21b88 --- /dev/null +++ b/tests/ui/consts/const-eval/const_fn_target_feature.rs @@ -0,0 +1,17 @@ +// only-x86_64 +// compile-flags:-C target-feature=+ssse3 + +#![crate_type = "lib"] + +// ok (ssse3 enabled at compile time) +const A: () = unsafe { ssse3_fn() }; + +// error (avx2 not enabled at compile time) +const B: () = unsafe { avx2_fn() }; +//~^ ERROR evaluation of constant value failed + +#[target_feature(enable = "ssse3")] +const unsafe fn ssse3_fn() {} + +#[target_feature(enable = "avx2")] +const unsafe fn avx2_fn() {} diff --git a/tests/ui/consts/const-eval/const_fn_target_feature.stderr b/tests/ui/consts/const-eval/const_fn_target_feature.stderr new file mode 100644 index 0000000000000..36918b52cd3af --- /dev/null +++ b/tests/ui/consts/const-eval/const_fn_target_feature.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/const_fn_target_feature.rs:10:24 + | +LL | const B: () = unsafe { avx2_fn() }; + | ^^^^^^^^^ calling a function that requires unavailable target features: avx2 + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. From ae2a72deaa4c99468edbcb821b6a9b07ab94fe4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 16 Jul 2023 16:07:55 +0200 Subject: [PATCH 3/3] Refactor checking function target features during const-eval * Split into its own function * Do not build a `Vec` of unavailable features --- .../src/interpret/terminator.rs | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index b6eea191aa67c..7964c6be008cb 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -503,24 +503,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - let missing_features: Vec<_> = attrs - .target_features - .iter() - .copied() - .filter(|feature| !self.tcx.sess.target_features.contains(feature)) - .collect(); - if !missing_features.is_empty() { - let mut missing_features_str = String::from(missing_features[0].as_str()); - for missing_feature in missing_features[1..].iter() { - missing_features_str.push(','); - missing_features_str.push_str(missing_feature.as_str()); - } - throw_ub_custom!( - fluent::const_eval_unavailable_target_features_for_fn, - unavailable_feats = missing_features_str, - ); - } + // Check that all target features required by the callee (i.e., from + // the attribute `#[target_feature(enable = ...)]`) are enabled at + // compile time. + self.check_fn_target_features(instance)?; if !callee_fn_abi.can_unwind { // The callee cannot unwind, so force the `Unreachable` unwind handling. @@ -805,6 +791,31 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { + let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + if attrs + .target_features + .iter() + .any(|feature| !self.tcx.sess.target_features.contains(feature)) + { + throw_ub_custom!( + fluent::const_eval_unavailable_target_features_for_fn, + unavailable_feats = attrs + .target_features + .iter() + .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) + .fold(String::new(), |mut s, feature| { + if !s.is_empty() { + s.push_str(", "); + } + s.push_str(feature.as_str()); + s + }), + ); + } + Ok(()) + } + fn drop_in_place( &mut self, place: &PlaceTy<'tcx, M::Provenance>,