Implement runtime checks for compilation settings (#3899)

* Implement runtime checks for compilation settings

This commit fills out a few FIXME annotations by implementing run-time
checks that when a `Module` is created it has compatible codegen
settings for the current host (as `Module` is proof of "this code can
run"). This is done by implementing new `Engine`-level methods which
validate compiler settings. These settings are validated on
`Module::new` as well as when loading serialized modules.

Settings are split into two categories, one for "shared" top-level
settings and one for ISA-specific settings. Both categories now have
allow-lists hardcoded into `Engine` which indicate the acceptable values
for each setting (if applicable). ISA-specific settings are checked with
the Rust standard library's `std::is_x86_feature_detected!` macro. Other
macros for other platforms are not stable at this time but can be added
here if necessary.

Closes #3897

* Fix fall-through logic to actually be correct

* Use a `OnceCell`, not an `AtomicBool`

* Fix some broken tests
This commit is contained in:
Alex Crichton
2022-03-09 09:46:25 -06:00
committed by GitHub
parent 9137b4a50e
commit 2f4419cc6c
7 changed files with 272 additions and 119 deletions

View File

@@ -10,9 +10,11 @@ fn checks_incompatible_target() -> Result<()> {
"(module)",
) {
Ok(_) => unreachable!(),
Err(e) => assert!(e
.to_string()
.contains("configuration does not match the host")),
Err(e) => assert!(
format!("{:?}", e).contains("configuration does not match the host"),
"bad error: {:?}",
e
),
}
Ok(())
@@ -31,33 +33,20 @@ fn caches_across_engines() {
let res = Module::deserialize(&Engine::default(), &bytes);
assert!(res.is_ok());
// differ in shared cranelift flags
// differ in runtime settings
let res = Module::deserialize(
&Engine::new(Config::new().cranelift_nan_canonicalization(true)).unwrap(),
&Engine::new(Config::new().static_memory_maximum_size(0)).unwrap(),
&bytes,
);
assert!(res.is_err());
// differ in cranelift settings
// differ in wasm features enabled (which can affect
// runtime/compilation settings)
let res = Module::deserialize(
&Engine::new(Config::new().cranelift_opt_level(OptLevel::None)).unwrap(),
&Engine::new(Config::new().wasm_simd(false)).unwrap(),
&bytes,
);
assert!(res.is_err());
// Missing required cpu flags
if cfg!(target_arch = "x86_64") {
let res = Module::deserialize(
&Engine::new(
Config::new()
.target(&target_lexicon::Triple::host().to_string())
.unwrap(),
)
.unwrap(),
&bytes,
);
assert!(res.is_err());
}
}
}

View File

@@ -66,7 +66,7 @@ fn test_module_serialize_fail() -> Result<()> {
)?;
let mut config = Config::new();
config.cranelift_opt_level(OptLevel::None);
config.static_memory_maximum_size(0);
let mut store = Store::new(&Engine::new(&config)?, ());
match unsafe { deserialize_and_instantiate(&mut store, &buffer) } {
Ok(_) => bail!("expected failure at deserialization"),

View File

@@ -12,6 +12,10 @@ include!(concat!(env!("OUT_DIR"), "/wast_testsuite_tests.rs"));
// function which actually executes the `wast` test suite given the `strategy`
// to compile it.
fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()> {
match strategy {
Strategy::Cranelift => {}
_ => unimplemented!(),
}
let wast = Path::new(wast);
let simd = feature_found(wast, "simd");
@@ -26,7 +30,6 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
.wasm_module_linking(module_linking)
.wasm_threads(threads)
.wasm_memory64(memory64)
.strategy(strategy)?
.cranelift_debug_verifier(true);
if feature_found(wast, "canonicalize-nan") {