From 7211ace58a046e19b1e45355bd2d4a62c3b19387 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Mon, 14 Jun 2021 12:37:25 +0100 Subject: [PATCH 01/20] cranelift: Initial fuzzer implementation --- Cargo.lock | 11 ++ cranelift/Cargo.toml | 1 + cranelift/fuzzgen/Cargo.toml | 24 ++++ cranelift/fuzzgen/LICENSE | 220 ++++++++++++++++++++++++++++++ cranelift/fuzzgen/README.md | 3 + cranelift/fuzzgen/src/gen-once.rs | 17 +++ cranelift/fuzzgen/src/lib.rs | 178 ++++++++++++++++++++++++ 7 files changed, 454 insertions(+) create mode 100644 cranelift/fuzzgen/Cargo.toml create mode 100644 cranelift/fuzzgen/LICENSE create mode 100644 cranelift/fuzzgen/README.md create mode 100644 cranelift/fuzzgen/src/gen-once.rs create mode 100644 cranelift/fuzzgen/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 32e3de6dc2ea..6b17d52a231b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,6 +640,16 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-fuzzgen" +version = "0.75.0" +dependencies = [ + "anyhow", + "arbitrary", + "cranelift", + "rand 0.8.3", +] + [[package]] name = "cranelift-interpreter" version = "0.75.0" @@ -747,6 +757,7 @@ dependencies = [ "cranelift-entity", "cranelift-filetests", "cranelift-frontend", + "cranelift-fuzzgen", "cranelift-interpreter", "cranelift-jit", "cranelift-module", diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index d3f11855bd6d..aad4dcadd1d8 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -29,6 +29,7 @@ cranelift-object = { path = "object", version = "0.75.0" } cranelift-jit = { path = "jit", version = "0.75.0" } cranelift-preopt = { path = "preopt", version = "0.75.0" } cranelift = { path = "umbrella", version = "0.75.0" } +cranelift-fuzzgen = { path = "fuzzgen", version = "0.75.0" } filecheck = "0.5.0" log = "0.4.8" termcolor = "1.1.2" diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml new file mode 100644 index 000000000000..a2d2dbf497e0 --- /dev/null +++ b/cranelift/fuzzgen/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "cranelift-fuzzgen" +version = "0.75.0" +authors = ["The Wasmtime Project Developers"] +description = "Cranelift module generator" +license = "Apache-2.0 WITH LLVM-exception" +repository = "https://github.com/bytecodealliance/wasmtime" +readme = "README.md" +edition = "2018" +publish=false + + +[[bin]] +name = "clif-util" +path = "src/gen-once.rs" + +[dependencies] +cranelift = { path = "../umbrella", version = "0.75.0" } + +anyhow = "1.0.19" +arbitrary = { version = "1.0.0", features = ["derive"] } + +# TODO: Remove this +rand = "0.8.0" diff --git a/cranelift/fuzzgen/LICENSE b/cranelift/fuzzgen/LICENSE new file mode 100644 index 000000000000..f9d81955f4bc --- /dev/null +++ b/cranelift/fuzzgen/LICENSE @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/cranelift/fuzzgen/README.md b/cranelift/fuzzgen/README.md new file mode 100644 index 000000000000..fdd7e3c79c08 --- /dev/null +++ b/cranelift/fuzzgen/README.md @@ -0,0 +1,3 @@ +# `cranelift-fuzzgen` + +This crate implements a generator to create random cranelift modules diff --git a/cranelift/fuzzgen/src/gen-once.rs b/cranelift/fuzzgen/src/gen-once.rs new file mode 100644 index 000000000000..ade34f496eb2 --- /dev/null +++ b/cranelift/fuzzgen/src/gen-once.rs @@ -0,0 +1,17 @@ +use arbitrary::Unstructured; + +use cranelift_fuzzgen::FuzzGen; +use rand::{thread_rng, Rng}; + +fn main() { + // let mut u = Unstructured::new(&[2, 5, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); + let mut data = [0u8; 128]; + thread_rng().fill(&mut data[..]); + + let mut u = Unstructured::new(&data); + + let mut fuzzgen = FuzzGen::new(&mut u); + let func = fuzzgen.generate_function().unwrap(); + + println!("{}", func.display(None)); +} diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs new file mode 100644 index 000000000000..e8cd95153480 --- /dev/null +++ b/cranelift/fuzzgen/src/lib.rs @@ -0,0 +1,178 @@ +use anyhow::{anyhow, Result}; +use arbitrary::{Arbitrary, Unstructured}; +use cranelift::codegen::ir::types::*; +use cranelift::codegen::{ir::Function, verify_function}; +use cranelift::prelude::isa::CallConv; +use cranelift::prelude::*; + +pub struct FuzzGen<'a> { + u: &'a mut Unstructured<'a>, + vars: Vec<(Type, Variable)>, +} + +impl<'a> FuzzGen<'a> { + pub fn new(u: &'a mut Unstructured<'a>) -> Self { + Self { u, vars: vec![] } + } + + fn generate_callconv(&mut self) -> Result { + // TODO: Generate random CallConvs per target + Ok(CallConv::Fast) + } + + fn generate_type(&mut self) -> Result { + // TODO: It would be nice if we could get these directly from cranelift + let scalars = [ + // IFLAGS, FFLAGS, + // B1, B8, B16, B32, B64, B128, + I8, I16, I32, I64, + // I128, + // F32, F64, + // R32, R64, + ]; + // TODO: vector types + + let ty = self.u.choose(&scalars[..])?; + Ok(*ty) + } + + fn generate_abi_param(&mut self) -> Result { + // TODO: Generate more advanced abi params (structs/purposes/extensions/etc...) + let ty = self.generate_type()?; + Ok(AbiParam::new(ty)) + } + + fn generate_signature(&mut self) -> Result { + let callconv = self.generate_callconv()?; + let mut sig = Signature::new(callconv); + + // TODO: Unconstrain this + for _ in 0..self.u.int_in_range(0..=8)? { + sig.params.push(self.generate_abi_param()?); + } + + for _ in 0..self.u.int_in_range(0..=8)? { + sig.returns.push(self.generate_abi_param()?); + } + + Ok(sig) + } + + /// Creates a new var + fn create_var(&mut self, ty: Type) -> Result { + let id = self.vars.len(); + let var = Variable::new(id); + self.vars.push((ty, var)); + Ok(var) + } + + // TODO: Rename this + fn vars_of_type(&self, ty: Type) -> Vec { + self.vars + .iter() + .filter(|(var_ty, _)| *var_ty == ty) + .map(|(_, v)| *v) + .collect() + } + + /// Get a variable of type `ty`, either reusing a old var, or generating a new one with a + /// `iconst`/`fconst`. + fn get_variable_of_type( + &mut self, + builder: &mut FunctionBuilder, + ty: Type, + ) -> Result { + // TODO: global vars + + let mut opts: Vec< + fn(fg: &mut FuzzGen, builder: &mut FunctionBuilder, ty: Type) -> Result, + > = Vec::with_capacity(2); + + // Generate new + opts.push(|fg, builder, ty| { + let imm64 = match ty { + I8 => fg.u.arbitrary::()? as i64, + I16 => fg.u.arbitrary::()? as i64, + I32 => fg.u.arbitrary::()? as i64, + I64 => fg.u.arbitrary::()?, + _ => unreachable!(), + }; + let var = fg.create_var(ty)?; + builder.declare_var(var, ty); + + let val = builder.ins().iconst(ty, imm64); + builder.def_var(var, val); + + Ok(var) + }); + + // Reuse var + if self.vars_of_type(ty).len() != 0 { + opts.push(|fg, builder, ty| { + let opts = fg.vars_of_type(ty); + let var = fg.u.choose(&opts[..])?; + Ok(*var) + }); + } + + let f = self.u.choose(&opts[..])?; + f(self, builder, ty) + } + + fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> { + let ret_params = builder.func.signature.returns.clone(); + + let vars = ret_params + .iter() + .map(|p| self.get_variable_of_type(builder, p.value_type)) + .collect::>>()?; + + let vals = vars + .into_iter() + .map(|v| builder.use_var(v)) + .collect::>(); + + builder.ins().return_(&vals[..]); + Ok(()) + } + + pub fn generate_function(&mut self) -> Result { + let sig = self.generate_signature()?; + + let mut fn_builder_ctx = FunctionBuilderContext::new(); + let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); + + let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx); + let block0 = builder.create_block(); + + let mut block_vars = vec![]; + for param in &sig.params { + let var = self.create_var(param.value_type)?; + builder.declare_var(var, param.value_type); + + block_vars.push(var); + } + builder.append_block_params_for_function_params(block0); + builder.switch_to_block(block0); + builder.seal_block(block0); + + for (i, _) in sig.params.iter().enumerate() { + let var = block_vars[i]; + let block_param = builder.block_params(block0)[i]; + builder.def_var(var, block_param); + let _ = builder.use_var(block_vars[i]); + } + + self.generate_return(&mut builder)?; + + builder.finalize(); + + let flags = settings::Flags::new(settings::builder()); + let res = verify_function(&func, &flags); + if let Err(errors) = res { + panic!("{}", errors); + } + + Ok(func) + } +} From 84b74f68327083042b1bc797e4e92dd156e7cfa0 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sat, 26 Jun 2021 11:43:22 +0100 Subject: [PATCH 02/20] cranelift: Generate multiple test cases in fuzzer --- cranelift/filetests/src/function_runner.rs | 4 + cranelift/filetests/src/lib.rs | 2 +- cranelift/fuzz/.gitignore | 4 + cranelift/fuzz/Cargo.lock | 546 +++++++++++++++++++++ cranelift/fuzz/Cargo.toml | 36 ++ cranelift/fuzz/fuzz_targets/fuzzgen.rs | 80 +++ cranelift/fuzzgen/src/gen-once.rs | 16 +- cranelift/fuzzgen/src/lib.rs | 54 +- 8 files changed, 732 insertions(+), 10 deletions(-) create mode 100644 cranelift/fuzz/.gitignore create mode 100644 cranelift/fuzz/Cargo.lock create mode 100644 cranelift/fuzz/Cargo.toml create mode 100644 cranelift/fuzz/fuzz_targets/fuzzgen.rs diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 1546cb14593b..1ac19b0ae09e 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -92,13 +92,17 @@ impl SingleFunctionCompiler { } #[derive(Error, Debug)] +/// TODO pub enum CompilationError { #[error("Cross-compilation not currently supported; use the host's default calling convention \ or remove the specified calling convention in the function signature to use the host's default.")] + /// TODO InvalidTargetIsa, #[error("Cranelift codegen error")] + /// TODO CodegenError(#[from] CodegenError), #[error("Memory mapping error")] + /// TODO IoError(#[from] std::io::Error), } diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs index fbf5340bdfbb..b17727da58de 100644 --- a/cranelift/filetests/src/lib.rs +++ b/cranelift/filetests/src/lib.rs @@ -30,7 +30,7 @@ use std::path::Path; use std::time; mod concurrent; -mod function_runner; +pub mod function_runner; mod match_directive; mod runner; mod runone; diff --git a/cranelift/fuzz/.gitignore b/cranelift/fuzz/.gitignore new file mode 100644 index 000000000000..572e03bdf321 --- /dev/null +++ b/cranelift/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/cranelift/fuzz/Cargo.lock b/cranelift/fuzz/Cargo.lock new file mode 100644 index 000000000000..98370946b26b --- /dev/null +++ b/cranelift/fuzz/Cargo.lock @@ -0,0 +1,546 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" + +[[package]] +name = "arbitrary" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "cc" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cranelift" +version = "0.75.0" +dependencies = [ + "cranelift-codegen", + "cranelift-frontend", +] + +[[package]] +name = "cranelift-bforest" +version = "0.75.0" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.75.0" +dependencies = [ + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli", + "log", + "regalloc", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.75.0" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.75.0" + +[[package]] +name = "cranelift-entity" +version = "0.75.0" + +[[package]] +name = "cranelift-filetests" +version = "0.73.0" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-frontend", + "cranelift-interpreter", + "cranelift-native", + "cranelift-preopt", + "cranelift-reader", + "file-per-thread-logger", + "filecheck", + "gimli", + "log", + "memmap2", + "num_cpus", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "cranelift-frontend" +version = "0.75.0" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-fuzzgen" +version = "0.75.0" +dependencies = [ + "anyhow", + "arbitrary", + "cranelift", + "rand", +] + +[[package]] +name = "cranelift-interpreter" +version = "0.75.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "log", + "smallvec", + "thiserror", +] + +[[package]] +name = "cranelift-native" +version = "0.75.0" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-preopt" +version = "0.75.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", +] + +[[package]] +name = "cranelift-reader" +version = "0.75.0" +dependencies = [ + "cranelift-codegen", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-tools-fuzz" +version = "0.0.0" +dependencies = [ + "arbitrary", + "cranelift", + "cranelift-filetests", + "cranelift-fuzzgen", + "cranelift-interpreter", + "libfuzzer-sys", +] + +[[package]] +name = "derive_arbitrary" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1281ee141df08871db9fe261ab5312179eac32d1e314134ceaa8dd7c042f5a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "filecheck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe00b427b7c4835f8b82170eb7b9a63634376b63d73b9a9093367e82570bbaa" +dependencies = [ + "regex", + "thiserror", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +dependencies = [ + "indexmap", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "libc" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regalloc" +version = "0.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" +dependencies = [ + "log", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "target-lexicon" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ae3b39281e4b14b8123bdbaddd472b7dfe215e444181f2f9d2443c2444f834" + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/cranelift/fuzz/Cargo.toml b/cranelift/fuzz/Cargo.toml new file mode 100644 index 000000000000..d6f302f95830 --- /dev/null +++ b/cranelift/fuzz/Cargo.toml @@ -0,0 +1,36 @@ + +[package] +name = "cranelift-tools-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +arbitrary = { version = "1.0.0", features = ["derive"] } + +[dependencies.cranelift-filetests] +path = "../filetests" + +[dependencies.cranelift-interpreter] +path = "../interpreter" + +[dependencies.cranelift-fuzzgen] +path = "../fuzzgen" + +[dependencies.cranelift] +path = "../umbrella" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "fuzzgen" +path = "fuzz_targets/fuzzgen.rs" +test = false +doc = false diff --git a/cranelift/fuzz/fuzz_targets/fuzzgen.rs b/cranelift/fuzz/fuzz_targets/fuzzgen.rs new file mode 100644 index 000000000000..b2589e2777db --- /dev/null +++ b/cranelift/fuzz/fuzz_targets/fuzzgen.rs @@ -0,0 +1,80 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use crate::codegen::ir::Function; +use arbitrary::Unstructured; +use cranelift::codegen::data_value::DataValue; +use cranelift::prelude::*; +use cranelift_filetests::function_runner::SingleFunctionCompiler; +use cranelift_fuzzgen::*; +use cranelift_interpreter::environment::FuncIndex; +use cranelift_interpreter::environment::FunctionStore; +use cranelift_interpreter::interpreter::{Interpreter, InterpreterState}; +use cranelift_interpreter::step::ControlFlow; +use cranelift_interpreter::step::CraneliftTrap; + +enum RunResult { + Success(Vec), + Trap(CraneliftTrap), + Error(Box), +} + +fn run_in_interpreter(func: &Function, args: &[DataValue]) -> RunResult { + let mut env = FunctionStore::default(); + env.add(func.name.to_string(), func); + + let state = InterpreterState::default().with_function_store(env); + let mut interpreter = Interpreter::new(state); + + // The entrypoint function is always 0 + let index = FuncIndex::from_u32(0); + let res = interpreter.call_by_index(index, args); + match res { + Ok(ControlFlow::Return(results)) => RunResult::Success(results.to_vec()), + Ok(ControlFlow::Trap(trap)) => RunResult::Trap(trap), + Ok(cf) => RunResult::Error(format!("Unrecognized exit ControlFlow: {:?}", cf).into()), + Err(e) => RunResult::Error(format!("InterpreterError: {:?}", e).into()), + } +} + +fn run_in_host(func: &Function, args: &[DataValue]) -> RunResult { + let mut compiler = SingleFunctionCompiler::with_default_host_isa(); + + match compiler.compile(func.clone()) { + Ok(compiled_fn) => { + // TODO: What happens if we trap here? + let res = compiled_fn.call(args); + RunResult::Success(res) + } + Err(e) => RunResult::Error(Box::new(e)), + } +} + +fuzz_target!(|data: &[u8]| { + let mut u = Unstructured::new(data); + + let mut fuzzgen = FuzzGen::new(&mut u); + let (func, args): (Function, Vec) = match fuzzgen.generate_test() { + Ok(test) => test, + + // arbitrary Errors mean that the fuzzer didn't give us enough input data + Err(e) if e.is::() => { + return; + } + Err(e) => std::panic::panic_any(e), + }; + + let int_res = run_in_interpreter(&func, &args[..]); + if let RunResult::Error(e) = int_res { + panic!("interpreter failed: {}", e); + } + + let host_res = run_in_host(&func, &args[..]); + if let RunResult::Error(e) = host_res { + panic!("host failed: {}", e); + } + + // match (int_res, host_res) { + // + // } +}); diff --git a/cranelift/fuzzgen/src/gen-once.rs b/cranelift/fuzzgen/src/gen-once.rs index ade34f496eb2..d2edff0e4a76 100644 --- a/cranelift/fuzzgen/src/gen-once.rs +++ b/cranelift/fuzzgen/src/gen-once.rs @@ -4,14 +4,22 @@ use cranelift_fuzzgen::FuzzGen; use rand::{thread_rng, Rng}; fn main() { - // let mut u = Unstructured::new(&[2, 5, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); + // let mut u = Unstructured::new(&[0, 0, 2u8]); let mut data = [0u8; 128]; thread_rng().fill(&mut data[..]); - let mut u = Unstructured::new(&data); let mut fuzzgen = FuzzGen::new(&mut u); - let func = fuzzgen.generate_function().unwrap(); + let testcase = fuzzgen.generate_test().unwrap(); + + println!("{}", testcase.func.display(None)); + for input in testcase.inputs { + let fmt_inputs = input + .iter() + .map(|i| format!("{}", i)) + .collect::>() + .join(", "); - println!("{}", func.display(None)); + println!("; run: %{}({}) == ?", testcase.func.name, fmt_inputs); + } } diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index e8cd95153480..310cc6235674 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -1,10 +1,18 @@ -use anyhow::{anyhow, Result}; -use arbitrary::{Arbitrary, Unstructured}; +use anyhow::Result; +use arbitrary::Unstructured; +use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::types::*; use cranelift::codegen::{ir::Function, verify_function}; use cranelift::prelude::isa::CallConv; use cranelift::prelude::*; +pub type TestCaseInput = Vec; + +pub struct TestCase { + pub func: Function, + pub inputs: Vec, +} + pub struct FuzzGen<'a> { u: &'a mut Unstructured<'a>, vars: Vec<(Type, Variable)>, @@ -17,7 +25,8 @@ impl<'a> FuzzGen<'a> { fn generate_callconv(&mut self) -> Result { // TODO: Generate random CallConvs per target - Ok(CallConv::Fast) + // Ok(CallConv::Fast) + Ok(CallConv::SystemV) } fn generate_type(&mut self) -> Result { @@ -108,7 +117,7 @@ impl<'a> FuzzGen<'a> { // Reuse var if self.vars_of_type(ty).len() != 0 { - opts.push(|fg, builder, ty| { + opts.push(|fg, _, ty| { let opts = fg.vars_of_type(ty); let var = fg.u.choose(&opts[..])?; Ok(*var) @@ -136,7 +145,7 @@ impl<'a> FuzzGen<'a> { Ok(()) } - pub fn generate_function(&mut self) -> Result { + fn generate_function(&mut self) -> Result { let sig = self.generate_signature()?; let mut fn_builder_ctx = FunctionBuilderContext::new(); @@ -163,6 +172,7 @@ impl<'a> FuzzGen<'a> { let _ = builder.use_var(block_vars[i]); } + // TODO: We should make this part of the regular instruction selection self.generate_return(&mut builder)?; builder.finalize(); @@ -175,4 +185,38 @@ impl<'a> FuzzGen<'a> { Ok(func) } + + fn generate_test_inputs(&mut self, signature: &Signature) -> Result> { + // TODO: More test cases? + let num_tests = self.u.int_in_range(1..=10)?; + let mut inputs = Vec::with_capacity(num_tests); + + for _ in 0..num_tests { + let test_args = signature + .params + .iter() + .map(|p| { + let imm64 = match p.value_type { + I8 => self.u.arbitrary::()? as i64, + I16 => self.u.arbitrary::()? as i64, + I32 => self.u.arbitrary::()? as i64, + I64 => self.u.arbitrary::()?, + _ => unreachable!(), + }; + Ok(DataValue::from_integer(imm64, p.value_type)?) + }) + .collect::>()?; + + inputs.push(test_args); + } + + Ok(inputs) + } + + pub fn generate_test(&mut self) -> Result { + let func = self.generate_function()?; + let inputs = self.generate_test_inputs(&func.signature)?; + + Ok(TestCase { func, inputs }) + } } From 766d661add3d596d2f2a89cf45dc0a380bf2c6bb Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sat, 26 Jun 2021 18:41:39 +0100 Subject: [PATCH 03/20] cranelift: Separate function generator in fuzzer --- cranelift/fuzzgen/src/function_generator.rs | 185 ++++++++++++++++++++ cranelift/fuzzgen/src/lib.rs | 173 ++---------------- 2 files changed, 195 insertions(+), 163 deletions(-) create mode 100644 cranelift/fuzzgen/src/function_generator.rs diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs new file mode 100644 index 000000000000..bb9e214b44b1 --- /dev/null +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -0,0 +1,185 @@ +use anyhow::Result; +use arbitrary::Unstructured; +use cranelift::codegen::ir::types::*; +use cranelift::codegen::ir::{AbiParam, ExternalName, Function, Signature, Type}; +use cranelift::codegen::isa::CallConv; +use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; +use cranelift::prelude::{EntityRef, InstBuilder}; + +pub struct FunctionGenerator<'r, 'data> +where + 'data: 'r, +{ + u: &'r mut Unstructured<'data>, + vars: Vec<(Type, Variable)>, +} + +impl<'r, 'data> FunctionGenerator<'r, 'data> +where + 'data: 'r, +{ + pub fn new(u: &'r mut Unstructured<'data>) -> Self { + Self { u, vars: vec![] } + } + + fn generate_callconv(&mut self) -> Result { + // TODO: Generate random CallConvs per target + // Ok(CallConv::Fast) + Ok(CallConv::SystemV) + } + + fn generate_type(&mut self) -> Result { + // TODO: It would be nice if we could get these directly from cranelift + let scalars = [ + // IFLAGS, FFLAGS, + // B1, B8, B16, B32, B64, B128, + I8, I16, I32, I64, + // I128, + // F32, F64, + // R32, R64, + ]; + // TODO: vector types + + let ty = self.u.choose(&scalars[..])?; + Ok(*ty) + } + + fn generate_abi_param(&mut self) -> Result { + // TODO: Generate more advanced abi params (structs/purposes/extensions/etc...) + let ty = self.generate_type()?; + Ok(AbiParam::new(ty)) + } + + fn generate_signature(&mut self) -> Result { + let callconv = self.generate_callconv()?; + let mut sig = Signature::new(callconv); + + // TODO: Unconstrain this + for _ in 0..self.u.int_in_range(0..=8)? { + sig.params.push(self.generate_abi_param()?); + } + + for _ in 0..self.u.int_in_range(0..=8)? { + sig.returns.push(self.generate_abi_param()?); + } + + Ok(sig) + } + + /// Creates a new var + fn create_var(&mut self, ty: Type) -> Result { + let id = self.vars.len(); + let var = Variable::new(id); + self.vars.push((ty, var)); + Ok(var) + } + + // TODO: Rename this + fn vars_of_type(&self, ty: Type) -> Vec { + self.vars + .iter() + .filter(|(var_ty, _)| *var_ty == ty) + .map(|(_, v)| *v) + .collect() + } + + /// Get a variable of type `ty`, either reusing a old var, or generating a new one with a + /// `iconst`/`fconst`. + fn get_variable_of_type( + &mut self, + builder: &mut FunctionBuilder, + ty: Type, + ) -> Result { + // TODO: global vars + + let mut opts: Vec< + fn( + fgen: &mut FunctionGenerator, + builder: &mut FunctionBuilder, + ty: Type, + ) -> Result, + > = Vec::with_capacity(2); + + // Generate new + opts.push(|fgen, builder, ty| { + let imm64 = match ty { + I8 => fgen.u.arbitrary::()? as i64, + I16 => fgen.u.arbitrary::()? as i64, + I32 => fgen.u.arbitrary::()? as i64, + I64 => fgen.u.arbitrary::()?, + _ => unreachable!(), + }; + let var = fgen.create_var(ty)?; + builder.declare_var(var, ty); + + let val = builder.ins().iconst(ty, imm64); + builder.def_var(var, val); + + Ok(var) + }); + + // Reuse var + if self.vars_of_type(ty).len() != 0 { + opts.push(|fg, _, ty| { + let opts = fg.vars_of_type(ty); + let var = fg.u.choose(&opts[..])?; + Ok(*var) + }); + } + + let f = self.u.choose(&opts[..])?; + f(self, builder, ty) + } + + fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> { + let ret_params = builder.func.signature.returns.clone(); + + let vars = ret_params + .iter() + .map(|p| self.get_variable_of_type(builder, p.value_type)) + .collect::>>()?; + + let vals = vars + .into_iter() + .map(|v| builder.use_var(v)) + .collect::>(); + + builder.ins().return_(&vals[..]); + Ok(()) + } + + pub fn generate(mut self) -> Result { + let sig = self.generate_signature()?; + + let mut fn_builder_ctx = FunctionBuilderContext::new(); + let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); + + let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx); + let block0 = builder.create_block(); + + let mut block_vars = vec![]; + for param in &sig.params { + let var = self.create_var(param.value_type)?; + builder.declare_var(var, param.value_type); + + block_vars.push(var); + } + builder.append_block_params_for_function_params(block0); + builder.switch_to_block(block0); + builder.seal_block(block0); + + for (i, _) in sig.params.iter().enumerate() { + let var = block_vars[i]; + let block_param = builder.block_params(block0)[i]; + builder.def_var(var, block_param); + let _ = builder.use_var(block_vars[i]); + } + + // TODO: We should make this part of the regular instruction selection + self.generate_return(&mut builder)?; + + builder.finalize(); + + Ok(func) + } +} diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 310cc6235674..feadeb15064f 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -1,11 +1,13 @@ +use crate::function_generator::FunctionGenerator; use anyhow::Result; use arbitrary::Unstructured; use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::types::*; use cranelift::codegen::{ir::Function, verify_function}; -use cranelift::prelude::isa::CallConv; use cranelift::prelude::*; +mod function_generator; + pub type TestCaseInput = Vec; pub struct TestCase { @@ -23,167 +25,10 @@ impl<'a> FuzzGen<'a> { Self { u, vars: vec![] } } - fn generate_callconv(&mut self) -> Result { - // TODO: Generate random CallConvs per target - // Ok(CallConv::Fast) - Ok(CallConv::SystemV) - } - - fn generate_type(&mut self) -> Result { - // TODO: It would be nice if we could get these directly from cranelift - let scalars = [ - // IFLAGS, FFLAGS, - // B1, B8, B16, B32, B64, B128, - I8, I16, I32, I64, - // I128, - // F32, F64, - // R32, R64, - ]; - // TODO: vector types - - let ty = self.u.choose(&scalars[..])?; - Ok(*ty) - } - - fn generate_abi_param(&mut self) -> Result { - // TODO: Generate more advanced abi params (structs/purposes/extensions/etc...) - let ty = self.generate_type()?; - Ok(AbiParam::new(ty)) - } - - fn generate_signature(&mut self) -> Result { - let callconv = self.generate_callconv()?; - let mut sig = Signature::new(callconv); - - // TODO: Unconstrain this - for _ in 0..self.u.int_in_range(0..=8)? { - sig.params.push(self.generate_abi_param()?); - } - - for _ in 0..self.u.int_in_range(0..=8)? { - sig.returns.push(self.generate_abi_param()?); - } - - Ok(sig) - } - - /// Creates a new var - fn create_var(&mut self, ty: Type) -> Result { - let id = self.vars.len(); - let var = Variable::new(id); - self.vars.push((ty, var)); - Ok(var) - } - - // TODO: Rename this - fn vars_of_type(&self, ty: Type) -> Vec { - self.vars - .iter() - .filter(|(var_ty, _)| *var_ty == ty) - .map(|(_, v)| *v) - .collect() - } - - /// Get a variable of type `ty`, either reusing a old var, or generating a new one with a - /// `iconst`/`fconst`. - fn get_variable_of_type( - &mut self, - builder: &mut FunctionBuilder, - ty: Type, - ) -> Result { - // TODO: global vars - - let mut opts: Vec< - fn(fg: &mut FuzzGen, builder: &mut FunctionBuilder, ty: Type) -> Result, - > = Vec::with_capacity(2); - - // Generate new - opts.push(|fg, builder, ty| { - let imm64 = match ty { - I8 => fg.u.arbitrary::()? as i64, - I16 => fg.u.arbitrary::()? as i64, - I32 => fg.u.arbitrary::()? as i64, - I64 => fg.u.arbitrary::()?, - _ => unreachable!(), - }; - let var = fg.create_var(ty)?; - builder.declare_var(var, ty); - - let val = builder.ins().iconst(ty, imm64); - builder.def_var(var, val); - - Ok(var) - }); - - // Reuse var - if self.vars_of_type(ty).len() != 0 { - opts.push(|fg, _, ty| { - let opts = fg.vars_of_type(ty); - let var = fg.u.choose(&opts[..])?; - Ok(*var) - }); - } - - let f = self.u.choose(&opts[..])?; - f(self, builder, ty) - } - - fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> { - let ret_params = builder.func.signature.returns.clone(); - - let vars = ret_params - .iter() - .map(|p| self.get_variable_of_type(builder, p.value_type)) - .collect::>>()?; - - let vals = vars - .into_iter() - .map(|v| builder.use_var(v)) - .collect::>(); - - builder.ins().return_(&vals[..]); - Ok(()) - } - - fn generate_function(&mut self) -> Result { - let sig = self.generate_signature()?; - - let mut fn_builder_ctx = FunctionBuilderContext::new(); - let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); - - let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx); - let block0 = builder.create_block(); - - let mut block_vars = vec![]; - for param in &sig.params { - let var = self.create_var(param.value_type)?; - builder.declare_var(var, param.value_type); - - block_vars.push(var); - } - builder.append_block_params_for_function_params(block0); - builder.switch_to_block(block0); - builder.seal_block(block0); - - for (i, _) in sig.params.iter().enumerate() { - let var = block_vars[i]; - let block_param = builder.block_params(block0)[i]; - builder.def_var(var, block_param); - let _ = builder.use_var(block_vars[i]); - } - - // TODO: We should make this part of the regular instruction selection - self.generate_return(&mut builder)?; - - builder.finalize(); - + fn verify_function(&self, func: &Function) -> Result<()> { let flags = settings::Flags::new(settings::builder()); - let res = verify_function(&func, &flags); - if let Err(errors) = res { - panic!("{}", errors); - } - - Ok(func) + verify_function(&func, &flags)?; + Ok(()) } fn generate_test_inputs(&mut self, signature: &Signature) -> Result> { @@ -213,8 +58,10 @@ impl<'a> FuzzGen<'a> { Ok(inputs) } - pub fn generate_test(&mut self) -> Result { - let func = self.generate_function()?; + pub fn generate_test(&'a mut self) -> Result { + let func = FunctionGenerator::new(self.u).generate()?; + self.verify_function(&func)?; + let inputs = self.generate_test_inputs(&func.signature)?; Ok(TestCase { func, inputs }) From 227c1a030154fd46b09d65ce0081e8bd78affb76 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sat, 26 Jun 2021 23:29:35 +0100 Subject: [PATCH 04/20] cranelift: Insert random instructions in fuzzer --- cranelift/fuzz/fuzz_targets/fuzzgen.rs | 5 +- cranelift/fuzzgen/src/function_generator.rs | 102 ++++++++++++++++++-- cranelift/fuzzgen/src/gen-once.rs | 8 +- 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/cranelift/fuzz/fuzz_targets/fuzzgen.rs b/cranelift/fuzz/fuzz_targets/fuzzgen.rs index b2589e2777db..38ac339d90e2 100644 --- a/cranelift/fuzz/fuzz_targets/fuzzgen.rs +++ b/cranelift/fuzz/fuzz_targets/fuzzgen.rs @@ -54,7 +54,7 @@ fuzz_target!(|data: &[u8]| { let mut u = Unstructured::new(data); let mut fuzzgen = FuzzGen::new(&mut u); - let (func, args): (Function, Vec) = match fuzzgen.generate_test() { + let testcase = match fuzzgen.generate_test() { Ok(test) => test, // arbitrary Errors mean that the fuzzer didn't give us enough input data @@ -64,6 +64,9 @@ fuzz_target!(|data: &[u8]| { Err(e) => std::panic::panic_any(e), }; + let func = testcase.func; + let args = &testcase.inputs[0]; + let int_res = run_in_interpreter(&func, &args[..]); if let RunResult::Error(e) = int_res { panic!("interpreter failed: {}", e); diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index bb9e214b44b1..ac14d16bf96a 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -1,11 +1,88 @@ use anyhow::Result; use arbitrary::Unstructured; use cranelift::codegen::ir::types::*; -use cranelift::codegen::ir::{AbiParam, ExternalName, Function, Signature, Type}; +use cranelift::codegen::ir::{AbiParam, ExternalName, Function, Opcode, Signature, Type}; use cranelift::codegen::isa::CallConv; use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use cranelift::prelude::{EntityRef, InstBuilder}; +fn insert_opcode_arity_0( + _fgen: &mut FunctionGenerator, + builder: &mut FunctionBuilder, + opcode: Opcode, + _args: &'static [Type], + _rets: &'static [Type], +) -> Result<()> { + builder.ins().NullAry(opcode, INVALID); + Ok(()) +} + +fn insert_opcode_arity_2( + fgen: &mut FunctionGenerator, + builder: &mut FunctionBuilder, + opcode: Opcode, + args: &'static [Type], + rets: &'static [Type], +) -> Result<()> { + let arg0 = fgen.get_variable_of_type(builder, args[0])?; + let arg0 = builder.use_var(arg0); + + let arg1 = fgen.get_variable_of_type(builder, args[1])?; + let arg1 = builder.use_var(arg1); + + let typevar = rets[0]; + let (inst, dfg) = builder.ins().Binary(opcode, typevar, arg0, arg1); + + let val = dfg.first_result(inst); + let var = fgen.create_var(builder, typevar)?; + builder.def_var(var, val); + + Ok(()) +} + +type OpcodeInserter = fn( + fgen: &mut FunctionGenerator, + builder: &mut FunctionBuilder, + Opcode, + &'static [Type], + &'static [Type], +) -> Result<()>; + +// TODO: Do we have a way to get this information automatically? +const OPCODE_SIGNATURES: &'static [( + Opcode, + &'static [Type], // Args + &'static [Type], // Rets + OpcodeInserter, +)] = &[ + (Opcode::Nop, &[], &[], insert_opcode_arity_0), + // Iadd + (Opcode::Iadd, &[I8, I8], &[I8], insert_opcode_arity_2), + (Opcode::Iadd, &[I16, I16], &[I16], insert_opcode_arity_2), + (Opcode::Iadd, &[I32, I32], &[I32], insert_opcode_arity_2), + (Opcode::Iadd, &[I64, I64], &[I64], insert_opcode_arity_2), + // Isub + (Opcode::Isub, &[I8, I8], &[I8], insert_opcode_arity_2), + (Opcode::Isub, &[I16, I16], &[I16], insert_opcode_arity_2), + (Opcode::Isub, &[I32, I32], &[I32], insert_opcode_arity_2), + (Opcode::Isub, &[I64, I64], &[I64], insert_opcode_arity_2), + // Imul + (Opcode::Imul, &[I8, I8], &[I8], insert_opcode_arity_2), + (Opcode::Imul, &[I16, I16], &[I16], insert_opcode_arity_2), + (Opcode::Imul, &[I32, I32], &[I32], insert_opcode_arity_2), + (Opcode::Imul, &[I64, I64], &[I64], insert_opcode_arity_2), + // Udiv + (Opcode::Udiv, &[I8, I8], &[I8], insert_opcode_arity_2), + (Opcode::Udiv, &[I16, I16], &[I16], insert_opcode_arity_2), + (Opcode::Udiv, &[I32, I32], &[I32], insert_opcode_arity_2), + (Opcode::Udiv, &[I64, I64], &[I64], insert_opcode_arity_2), + // Sdiv + (Opcode::Sdiv, &[I8, I8], &[I8], insert_opcode_arity_2), + (Opcode::Sdiv, &[I16, I16], &[I16], insert_opcode_arity_2), + (Opcode::Sdiv, &[I32, I32], &[I32], insert_opcode_arity_2), + (Opcode::Sdiv, &[I64, I64], &[I64], insert_opcode_arity_2), +]; + pub struct FunctionGenerator<'r, 'data> where 'data: 'r, @@ -67,9 +144,10 @@ where } /// Creates a new var - fn create_var(&mut self, ty: Type) -> Result { + fn create_var(&mut self, builder: &mut FunctionBuilder, ty: Type) -> Result { let id = self.vars.len(); let var = Variable::new(id); + builder.declare_var(var, ty); self.vars.push((ty, var)); Ok(var) } @@ -109,9 +187,7 @@ where I64 => fgen.u.arbitrary::()?, _ => unreachable!(), }; - let var = fgen.create_var(ty)?; - builder.declare_var(var, ty); - + let var = fgen.create_var(builder, ty)?; let val = builder.ins().iconst(ty, imm64); builder.def_var(var, val); @@ -148,6 +224,12 @@ where Ok(()) } + /// Inserts a random instruction into the block + fn generate_instruction(&mut self, builder: &mut FunctionBuilder) -> Result<()> { + let (op, args, rets, inserter) = *self.u.choose(OPCODE_SIGNATURES)?; + inserter(self, builder, op, args, rets) + } + pub fn generate(mut self) -> Result { let sig = self.generate_signature()?; @@ -159,15 +241,14 @@ where let mut block_vars = vec![]; for param in &sig.params { - let var = self.create_var(param.value_type)?; - builder.declare_var(var, param.value_type); - + let var = self.create_var(&mut builder, param.value_type)?; block_vars.push(var); } builder.append_block_params_for_function_params(block0); builder.switch_to_block(block0); builder.seal_block(block0); + // TODO: Cleanup for (i, _) in sig.params.iter().enumerate() { let var = block_vars[i]; let block_param = builder.block_params(block0)[i]; @@ -175,6 +256,11 @@ where let _ = builder.use_var(block_vars[i]); } + // TODO: Unconstrain this + for _ in 0..self.u.int_in_range(0..=16)? { + self.generate_instruction(&mut builder)?; + } + // TODO: We should make this part of the regular instruction selection self.generate_return(&mut builder)?; diff --git a/cranelift/fuzzgen/src/gen-once.rs b/cranelift/fuzzgen/src/gen-once.rs index d2edff0e4a76..54cf7743d143 100644 --- a/cranelift/fuzzgen/src/gen-once.rs +++ b/cranelift/fuzzgen/src/gen-once.rs @@ -4,10 +4,10 @@ use cranelift_fuzzgen::FuzzGen; use rand::{thread_rng, Rng}; fn main() { - // let mut u = Unstructured::new(&[0, 0, 2u8]); - let mut data = [0u8; 128]; - thread_rng().fill(&mut data[..]); - let mut u = Unstructured::new(&data); + let mut u = Unstructured::new(&[171u8, 171, 188, 56, 56, 56, 171, 56]); + // let mut data = [0u8; 1024]; + // thread_rng().fill(&mut data[..]); + // let mut u = Unstructured::new(&data); let mut fuzzgen = FuzzGen::new(&mut u); let testcase = fuzzgen.generate_test().unwrap(); From 6c9a3517a35439e50637d0d189c26d0a6ac36631 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 00:46:18 +0100 Subject: [PATCH 05/20] cranelift: Rename gen_testcase --- cranelift/fuzzgen/Cargo.toml | 4 ++-- cranelift/fuzzgen/src/function_generator.rs | 2 -- .../fuzzgen/src/{gen-once.rs => gen_testcase.rs} | 14 ++++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) rename cranelift/fuzzgen/src/{gen-once.rs => gen_testcase.rs} (58%) diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index a2d2dbf497e0..3cae67ef4ba3 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -11,8 +11,8 @@ publish=false [[bin]] -name = "clif-util" -path = "src/gen-once.rs" +name = "gen_testcase" +path = "src/gen_testcase.rs" [dependencies] cranelift = { path = "../umbrella", version = "0.75.0" } diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index ac14d16bf96a..a0619885d7bc 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -101,7 +101,6 @@ where fn generate_callconv(&mut self) -> Result { // TODO: Generate random CallConvs per target - // Ok(CallConv::Fast) Ok(CallConv::SystemV) } @@ -152,7 +151,6 @@ where Ok(var) } - // TODO: Rename this fn vars_of_type(&self, ty: Type) -> Vec { self.vars .iter() diff --git a/cranelift/fuzzgen/src/gen-once.rs b/cranelift/fuzzgen/src/gen_testcase.rs similarity index 58% rename from cranelift/fuzzgen/src/gen-once.rs rename to cranelift/fuzzgen/src/gen_testcase.rs index 54cf7743d143..75cba2e8d6d4 100644 --- a/cranelift/fuzzgen/src/gen-once.rs +++ b/cranelift/fuzzgen/src/gen_testcase.rs @@ -4,10 +4,16 @@ use cranelift_fuzzgen::FuzzGen; use rand::{thread_rng, Rng}; fn main() { - let mut u = Unstructured::new(&[171u8, 171, 188, 56, 56, 56, 171, 56]); - // let mut data = [0u8; 1024]; - // thread_rng().fill(&mut data[..]); - // let mut u = Unstructured::new(&data); + let data = if let Some(file) = std::env::args().nth(1) { + println!("; input file: {}", file); + std::fs::read(file).unwrap() + } else { + println!("; no input file, generating random bytes"); + let mut data = [0u8; 4096]; + thread_rng().fill(&mut data[..]); + Vec::from(data) + }; + let mut u = Unstructured::new(&data[..]); let mut fuzzgen = FuzzGen::new(&mut u); let testcase = fuzzgen.generate_test().unwrap(); From a26851c2cb78651e6d521b6ca5c1cf11b46b3604 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 17:04:11 +0100 Subject: [PATCH 06/20] cranelift: Implement div for unsigned values in interpreter --- cranelift/interpreter/src/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cranelift/interpreter/src/value.rs b/cranelift/interpreter/src/value.rs index 18dcf818ce98..b0d00185945a 100644 --- a/cranelift/interpreter/src/value.rs +++ b/cranelift/interpreter/src/value.rs @@ -284,7 +284,7 @@ impl Value for DataValue { } fn div(self, other: Self) -> ValueResult { - binary_match!(/(&self, &other); [I8, I16, I32, I64]) + binary_match!(/(&self, &other); [I8, I16, I32, I64, U8, U16, U32, U64]) } fn rem(self, other: Self) -> ValueResult { From 945e57017a498431b09f99a7a0ed409f2fe4c2b1 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 17:07:43 +0100 Subject: [PATCH 07/20] cranelift: Run all test cases in fuzzer --- cranelift/fuzz/fuzz_targets/fuzzgen.rs | 70 ++++++++++++++------------ cranelift/interpreter/src/step.rs | 2 +- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/cranelift/fuzz/fuzz_targets/fuzzgen.rs b/cranelift/fuzz/fuzz_targets/fuzzgen.rs index 38ac339d90e2..0fbb92a6bd27 100644 --- a/cranelift/fuzz/fuzz_targets/fuzzgen.rs +++ b/cranelift/fuzz/fuzz_targets/fuzzgen.rs @@ -5,7 +5,7 @@ use crate::codegen::ir::Function; use arbitrary::Unstructured; use cranelift::codegen::data_value::DataValue; use cranelift::prelude::*; -use cranelift_filetests::function_runner::SingleFunctionCompiler; +use cranelift_filetests::function_runner::{CompiledFunction, SingleFunctionCompiler}; use cranelift_fuzzgen::*; use cranelift_interpreter::environment::FuncIndex; use cranelift_interpreter::environment::FunctionStore; @@ -19,16 +19,11 @@ enum RunResult { Error(Box), } -fn run_in_interpreter(func: &Function, args: &[DataValue]) -> RunResult { - let mut env = FunctionStore::default(); - env.add(func.name.to_string(), func); - - let state = InterpreterState::default().with_function_store(env); - let mut interpreter = Interpreter::new(state); - +fn run_in_interpreter(interpreter: &mut Interpreter, args: &[DataValue]) -> RunResult { // The entrypoint function is always 0 let index = FuncIndex::from_u32(0); let res = interpreter.call_by_index(index, args); + match res { Ok(ControlFlow::Return(results)) => RunResult::Success(results.to_vec()), Ok(ControlFlow::Trap(trap)) => RunResult::Trap(trap), @@ -37,17 +32,10 @@ fn run_in_interpreter(func: &Function, args: &[DataValue]) -> RunResult { } } -fn run_in_host(func: &Function, args: &[DataValue]) -> RunResult { - let mut compiler = SingleFunctionCompiler::with_default_host_isa(); - - match compiler.compile(func.clone()) { - Ok(compiled_fn) => { - // TODO: What happens if we trap here? - let res = compiled_fn.call(args); - RunResult::Success(res) - } - Err(e) => RunResult::Error(Box::new(e)), - } +fn run_in_host(compiled_fn: &CompiledFunction, args: &[DataValue]) -> RunResult { + // TODO: What happens if we trap here? + let res = compiled_fn.call(args); + RunResult::Success(res) } fuzz_target!(|data: &[u8]| { @@ -64,20 +52,38 @@ fuzz_target!(|data: &[u8]| { Err(e) => std::panic::panic_any(e), }; - let func = testcase.func; - let args = &testcase.inputs[0]; + let mut interpreter = { + let mut env = FunctionStore::default(); + env.add(testcase.func.name.to_string(), &testcase.func); - let int_res = run_in_interpreter(&func, &args[..]); - if let RunResult::Error(e) = int_res { - panic!("interpreter failed: {}", e); - } + let state = InterpreterState::default().with_function_store(env); + let interpreter = Interpreter::new(state); + interpreter + }; - let host_res = run_in_host(&func, &args[..]); - if let RunResult::Error(e) = host_res { - panic!("host failed: {}", e); - } + // Native fn + let mut host_compiler = SingleFunctionCompiler::with_default_host_isa(); + let compiled_fn = host_compiler.compile(testcase.func.clone()).unwrap(); + + for args in &testcase.inputs { + let int_res = run_in_interpreter(&mut interpreter, args); + if let RunResult::Error(e) = int_res { + panic!("interpreter failed: {}", e); + } - // match (int_res, host_res) { - // - // } + let host_res = run_in_host(&compiled_fn, args); + if let RunResult::Error(e) = host_res { + panic!("host failed: {}", e); + } + + match (int_res, host_res) { + (RunResult::Success(lhs), RunResult::Success(rhs)) if lhs == rhs => { + return; + } + (RunResult::Trap(lhs), RunResult::Trap(rhs)) if lhs == rhs => { + return; + } + _ => panic!("Host and Interpreter disagree"), + } + } }); diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index fb40583f0c43..4b7887d6c65e 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -663,7 +663,7 @@ impl<'a, V> ControlFlow<'a, V> { } } -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] pub enum CraneliftTrap { #[error("user code: {0}")] User(TrapCode), From db442543d21d6899fd46e3701c2fc4a9344972cb Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 17:15:35 +0100 Subject: [PATCH 08/20] cranelift: Comment options in function_runner --- cranelift/filetests/src/function_runner.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 1ac19b0ae09e..034753fd8f46 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -92,17 +92,17 @@ impl SingleFunctionCompiler { } #[derive(Error, Debug)] -/// TODO +/// Compilation Error when compiling a function pub enum CompilationError { #[error("Cross-compilation not currently supported; use the host's default calling convention \ or remove the specified calling convention in the function signature to use the host's default.")] - /// TODO + /// This Target ISA is invalid for the current host InvalidTargetIsa, #[error("Cranelift codegen error")] - /// TODO + /// Cranelift codegen error CodegenError(#[from] CodegenError), #[error("Memory mapping error")] - /// TODO + /// Memory mapping error IoError(#[from] std::io::Error), } From bb72611e0e78638256ab3e2d34943e87409c1314 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 17:21:25 +0100 Subject: [PATCH 09/20] cranelift: Improve fuzzgen README.md --- cranelift/fuzzgen/Cargo.toml | 2 -- cranelift/fuzzgen/README.md | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index 3cae67ef4ba3..a3b1cae2aa33 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -19,6 +19,4 @@ cranelift = { path = "../umbrella", version = "0.75.0" } anyhow = "1.0.19" arbitrary = { version = "1.0.0", features = ["derive"] } - -# TODO: Remove this rand = "0.8.0" diff --git a/cranelift/fuzzgen/README.md b/cranelift/fuzzgen/README.md index fdd7e3c79c08..7128f05e4d8a 100644 --- a/cranelift/fuzzgen/README.md +++ b/cranelift/fuzzgen/README.md @@ -1,3 +1,24 @@ # `cranelift-fuzzgen` This crate implements a generator to create random cranelift modules + +## `gen-testcase` + +This is a util that allows you to quickly test changes in the fuzzgen library. + +It can be run without input, in which case it will generate a random cranelift module, a few test cases, +and will print the result as a clif file to stdout. + +You can run it in this mode by running the following command in the current directory: +``` +cargo run --bin gen_testcase +``` + + +If you pass in a fuzzer artifact file, it will generate the clif module and test inputs +for that fuzzer run. + +You can run it in this mode by running the following command in the current directory: +``` +cargo run --bin gen_testcase ../fuzz/artifacts/fuzzgen/crash-1ef624482d620e43e6d34da1afe46267fc695d9b +``` From d0b1d12329188cc0625761b317e3c64b7653a23a Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 17:46:46 +0100 Subject: [PATCH 10/20] cranelift: Fuzzgen remove unused variable --- cranelift/fuzzgen/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index feadeb15064f..b246f3d37897 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -17,12 +17,11 @@ pub struct TestCase { pub struct FuzzGen<'a> { u: &'a mut Unstructured<'a>, - vars: Vec<(Type, Variable)>, } impl<'a> FuzzGen<'a> { pub fn new(u: &'a mut Unstructured<'a>) -> Self { - Self { u, vars: vec![] } + Self { u } } fn verify_function(&self, func: &Function) -> Result<()> { From 2e11524bf3b64939e4aee223c6c4d49bcda0aea7 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Sun, 27 Jun 2021 18:09:05 +0100 Subject: [PATCH 11/20] cranelift: Fuzzer code style fixes Thanks! @bjorn3 --- cranelift/filetests/src/function_runner.rs | 8 ++++---- cranelift/fuzz/.gitignore | 1 - cranelift/fuzz/Cargo.lock | 24 +++++++++++----------- cranelift/fuzz/Cargo.toml | 20 ++++++------------ cranelift/fuzz/fuzz_targets/fuzzgen.rs | 3 ++- 5 files changed, 24 insertions(+), 32 deletions(-) diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 034753fd8f46..4e5f7b48d943 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -91,18 +91,18 @@ impl SingleFunctionCompiler { } } -#[derive(Error, Debug)] /// Compilation Error when compiling a function +#[derive(Error, Debug)] pub enum CompilationError { + /// This Target ISA is invalid for the current host #[error("Cross-compilation not currently supported; use the host's default calling convention \ or remove the specified calling convention in the function signature to use the host's default.")] - /// This Target ISA is invalid for the current host InvalidTargetIsa, - #[error("Cranelift codegen error")] /// Cranelift codegen error + #[error("Cranelift codegen error")] CodegenError(#[from] CodegenError), - #[error("Memory mapping error")] /// Memory mapping error + #[error("Memory mapping error")] IoError(#[from] std::io::Error), } diff --git a/cranelift/fuzz/.gitignore b/cranelift/fuzz/.gitignore index 572e03bdf321..a0925114d619 100644 --- a/cranelift/fuzz/.gitignore +++ b/cranelift/fuzz/.gitignore @@ -1,4 +1,3 @@ - target corpus artifacts diff --git a/cranelift/fuzz/Cargo.lock b/cranelift/fuzz/Cargo.lock index 98370946b26b..830ec998e5b6 100644 --- a/cranelift/fuzz/Cargo.lock +++ b/cranelift/fuzz/Cargo.lock @@ -132,6 +132,18 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-fuzz" +version = "0.0.0" +dependencies = [ + "arbitrary", + "cranelift", + "cranelift-filetests", + "cranelift-fuzzgen", + "cranelift-interpreter", + "libfuzzer-sys", +] + [[package]] name = "cranelift-fuzzgen" version = "0.75.0" @@ -179,18 +191,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "cranelift-tools-fuzz" -version = "0.0.0" -dependencies = [ - "arbitrary", - "cranelift", - "cranelift-filetests", - "cranelift-fuzzgen", - "cranelift-interpreter", - "libfuzzer-sys", -] - [[package]] name = "derive_arbitrary" version = "1.0.1" diff --git a/cranelift/fuzz/Cargo.toml b/cranelift/fuzz/Cargo.toml index d6f302f95830..cb58536fe853 100644 --- a/cranelift/fuzz/Cargo.toml +++ b/cranelift/fuzz/Cargo.toml @@ -1,8 +1,7 @@ - [package] -name = "cranelift-tools-fuzz" +name = "cranelift-fuzz" version = "0.0.0" -authors = ["Automatically generated"] +authors = ["The Wasmtime Project Developers"] publish = false edition = "2018" @@ -13,17 +12,10 @@ cargo-fuzz = true libfuzzer-sys = "0.4" arbitrary = { version = "1.0.0", features = ["derive"] } -[dependencies.cranelift-filetests] -path = "../filetests" - -[dependencies.cranelift-interpreter] -path = "../interpreter" - -[dependencies.cranelift-fuzzgen] -path = "../fuzzgen" - -[dependencies.cranelift] -path = "../umbrella" +cranelift = { path = "../umbrella" } +cranelift-filetests = { path = "../filetests" } +cranelift-interpreter = { path = "../interpreter" } +cranelift-fuzzgen = { path = "../fuzzgen" } # Prevent this from interfering with workspaces [workspace] diff --git a/cranelift/fuzz/fuzz_targets/fuzzgen.rs b/cranelift/fuzz/fuzz_targets/fuzzgen.rs index 0fbb92a6bd27..4b6871c46091 100644 --- a/cranelift/fuzz/fuzz_targets/fuzzgen.rs +++ b/cranelift/fuzz/fuzz_targets/fuzzgen.rs @@ -1,9 +1,10 @@ #![no_main] + use libfuzzer_sys::fuzz_target; -use crate::codegen::ir::Function; use arbitrary::Unstructured; use cranelift::codegen::data_value::DataValue; +use cranelift::codegen::ir::Function; use cranelift::prelude::*; use cranelift_filetests::function_runner::{CompiledFunction, SingleFunctionCompiler}; use cranelift_fuzzgen::*; From de171e1c9bf8685dd22ed809dd2f9f16be4d1c25 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 10:17:04 +0100 Subject: [PATCH 12/20] cranelift: Fix nits in CLIF fuzzer Thanks @cfallin! --- cranelift/filetests/src/function_runner.rs | 8 ++++---- cranelift/fuzzgen/README.md | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cranelift/filetests/src/function_runner.rs b/cranelift/filetests/src/function_runner.rs index 4e5f7b48d943..a781481af255 100644 --- a/cranelift/filetests/src/function_runner.rs +++ b/cranelift/filetests/src/function_runner.rs @@ -91,17 +91,17 @@ impl SingleFunctionCompiler { } } -/// Compilation Error when compiling a function +/// Compilation Error when compiling a function. #[derive(Error, Debug)] pub enum CompilationError { - /// This Target ISA is invalid for the current host + /// This Target ISA is invalid for the current host. #[error("Cross-compilation not currently supported; use the host's default calling convention \ or remove the specified calling convention in the function signature to use the host's default.")] InvalidTargetIsa, - /// Cranelift codegen error + /// Cranelift codegen error. #[error("Cranelift codegen error")] CodegenError(#[from] CodegenError), - /// Memory mapping error + /// Memory mapping error. #[error("Memory mapping error")] IoError(#[from] std::io::Error), } diff --git a/cranelift/fuzzgen/README.md b/cranelift/fuzzgen/README.md index 7128f05e4d8a..eece73fd53f4 100644 --- a/cranelift/fuzzgen/README.md +++ b/cranelift/fuzzgen/README.md @@ -1,12 +1,12 @@ # `cranelift-fuzzgen` -This crate implements a generator to create random cranelift modules +This crate implements a generator to create random Cranelift modules. ## `gen-testcase` -This is a util that allows you to quickly test changes in the fuzzgen library. +This is a utility that allows you to quickly test changes in the fuzzgen library. -It can be run without input, in which case it will generate a random cranelift module, a few test cases, +It can be run without input, in which case it will generate a random Cranelift module, a few test cases, and will print the result as a clif file to stdout. You can run it in this mode by running the following command in the current directory: From b7679237035f0ff3ad1ccd5904d2df13d33b2fbc Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 11:18:48 +0100 Subject: [PATCH 13/20] cranelift: Implement Arbitrary for TestCase --- cranelift/fuzz/fuzz_targets/fuzzgen.rs | 18 +-------------- cranelift/fuzzgen/Cargo.toml | 2 +- cranelift/fuzzgen/src/lib.rs | 31 ++++++++++++++++++++------ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/cranelift/fuzz/fuzz_targets/fuzzgen.rs b/cranelift/fuzz/fuzz_targets/fuzzgen.rs index 4b6871c46091..e85b71827722 100644 --- a/cranelift/fuzz/fuzz_targets/fuzzgen.rs +++ b/cranelift/fuzz/fuzz_targets/fuzzgen.rs @@ -2,10 +2,7 @@ use libfuzzer_sys::fuzz_target; -use arbitrary::Unstructured; use cranelift::codegen::data_value::DataValue; -use cranelift::codegen::ir::Function; -use cranelift::prelude::*; use cranelift_filetests::function_runner::{CompiledFunction, SingleFunctionCompiler}; use cranelift_fuzzgen::*; use cranelift_interpreter::environment::FuncIndex; @@ -39,20 +36,7 @@ fn run_in_host(compiled_fn: &CompiledFunction, args: &[DataValue]) -> RunResult RunResult::Success(res) } -fuzz_target!(|data: &[u8]| { - let mut u = Unstructured::new(data); - - let mut fuzzgen = FuzzGen::new(&mut u); - let testcase = match fuzzgen.generate_test() { - Ok(test) => test, - - // arbitrary Errors mean that the fuzzer didn't give us enough input data - Err(e) if e.is::() => { - return; - } - Err(e) => std::panic::panic_any(e), - }; - +fuzz_target!(|testcase: TestCase| { let mut interpreter = { let mut env = FunctionStore::default(); env.add(testcase.func.name.to_string(), &testcase.func); diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index a3b1cae2aa33..82187d4fc309 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -18,5 +18,5 @@ path = "src/gen_testcase.rs" cranelift = { path = "../umbrella", version = "0.75.0" } anyhow = "1.0.19" -arbitrary = { version = "1.0.0", features = ["derive"] } +arbitrary = "1.0.0" rand = "0.8.0" diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index b246f3d37897..6eade7dcda8c 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -1,6 +1,6 @@ use crate::function_generator::FunctionGenerator; use anyhow::Result; -use arbitrary::Unstructured; +use arbitrary::{Arbitrary, Unstructured}; use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::types::*; use cranelift::codegen::{ir::Function, verify_function}; @@ -10,17 +10,34 @@ mod function_generator; pub type TestCaseInput = Vec; +#[derive(Debug)] pub struct TestCase { pub func: Function, + /// Generate multiple test inputs for each test case. + /// This allows us to get more coverage per compilation, which may be somewhat expensive. pub inputs: Vec, } -pub struct FuzzGen<'a> { - u: &'a mut Unstructured<'a>, +impl<'a> Arbitrary<'a> for TestCase { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + FuzzGen::new(u) + .generate_test() + .map_err(|_| arbitrary::Error::IncorrectFormat) + } +} + +pub struct FuzzGen<'r, 'data> +where + 'data: 'r, +{ + u: &'r mut Unstructured<'data>, } -impl<'a> FuzzGen<'a> { - pub fn new(u: &'a mut Unstructured<'a>) -> Self { +impl<'r, 'data> FuzzGen<'r, 'data> +where + 'data: 'r, +{ + pub fn new(u: &'r mut Unstructured<'data>) -> Self { Self { u } } @@ -57,8 +74,8 @@ impl<'a> FuzzGen<'a> { Ok(inputs) } - pub fn generate_test(&'a mut self) -> Result { - let func = FunctionGenerator::new(self.u).generate()?; + pub fn generate_test(mut self) -> Result { + let func = FunctionGenerator::new(&mut self.u).generate()?; self.verify_function(&func)?; let inputs = self.generate_test_inputs(&func.signature)?; From 820e5a499ade1579175f16cbca842d717ccf20b6 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 11:26:02 +0100 Subject: [PATCH 14/20] cranelift: Remove gen_testcase --- cranelift/fuzzgen/Cargo.toml | 4 ---- cranelift/fuzzgen/README.md | 21 ------------------ cranelift/fuzzgen/src/gen_testcase.rs | 31 --------------------------- 3 files changed, 56 deletions(-) delete mode 100644 cranelift/fuzzgen/src/gen_testcase.rs diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index 82187d4fc309..2ad85cd7b425 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -10,10 +10,6 @@ edition = "2018" publish=false -[[bin]] -name = "gen_testcase" -path = "src/gen_testcase.rs" - [dependencies] cranelift = { path = "../umbrella", version = "0.75.0" } diff --git a/cranelift/fuzzgen/README.md b/cranelift/fuzzgen/README.md index eece73fd53f4..84f8abf9bc9c 100644 --- a/cranelift/fuzzgen/README.md +++ b/cranelift/fuzzgen/README.md @@ -1,24 +1,3 @@ # `cranelift-fuzzgen` This crate implements a generator to create random Cranelift modules. - -## `gen-testcase` - -This is a utility that allows you to quickly test changes in the fuzzgen library. - -It can be run without input, in which case it will generate a random Cranelift module, a few test cases, -and will print the result as a clif file to stdout. - -You can run it in this mode by running the following command in the current directory: -``` -cargo run --bin gen_testcase -``` - - -If you pass in a fuzzer artifact file, it will generate the clif module and test inputs -for that fuzzer run. - -You can run it in this mode by running the following command in the current directory: -``` -cargo run --bin gen_testcase ../fuzz/artifacts/fuzzgen/crash-1ef624482d620e43e6d34da1afe46267fc695d9b -``` diff --git a/cranelift/fuzzgen/src/gen_testcase.rs b/cranelift/fuzzgen/src/gen_testcase.rs deleted file mode 100644 index 75cba2e8d6d4..000000000000 --- a/cranelift/fuzzgen/src/gen_testcase.rs +++ /dev/null @@ -1,31 +0,0 @@ -use arbitrary::Unstructured; - -use cranelift_fuzzgen::FuzzGen; -use rand::{thread_rng, Rng}; - -fn main() { - let data = if let Some(file) = std::env::args().nth(1) { - println!("; input file: {}", file); - std::fs::read(file).unwrap() - } else { - println!("; no input file, generating random bytes"); - let mut data = [0u8; 4096]; - thread_rng().fill(&mut data[..]); - Vec::from(data) - }; - let mut u = Unstructured::new(&data[..]); - - let mut fuzzgen = FuzzGen::new(&mut u); - let testcase = fuzzgen.generate_test().unwrap(); - - println!("{}", testcase.func.display(None)); - for input in testcase.inputs { - let fmt_inputs = input - .iter() - .map(|i| format!("{}", i)) - .collect::>() - .join(", "); - - println!("; run: %{}({}) == ?", testcase.func.name, fmt_inputs); - } -} From b54876cefea2c246e45ebff5ffd942fbdf717abf Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 11:51:35 +0100 Subject: [PATCH 15/20] cranelift: Move fuzzers to wasmtime fuzz directory --- Cargo.lock | 3 + cranelift/fuzz/.gitignore | 3 - cranelift/fuzz/Cargo.lock | 546 ------------------ cranelift/fuzz/Cargo.toml | 28 - fuzz/Cargo.toml | 9 + .../fuzz_targets/cranelift-fuzzgen.rs | 2 +- 6 files changed, 13 insertions(+), 578 deletions(-) delete mode 100644 cranelift/fuzz/.gitignore delete mode 100644 cranelift/fuzz/Cargo.lock delete mode 100644 cranelift/fuzz/Cargo.toml rename cranelift/fuzz/fuzz_targets/fuzzgen.rs => fuzz/fuzz_targets/cranelift-fuzzgen.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 6b17d52a231b..097a17768449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3687,6 +3687,9 @@ name = "wasmtime-fuzz" version = "0.0.0" dependencies = [ "cranelift-codegen", + "cranelift-filetests", + "cranelift-fuzzgen", + "cranelift-interpreter", "cranelift-reader", "cranelift-wasm", "libfuzzer-sys", diff --git a/cranelift/fuzz/.gitignore b/cranelift/fuzz/.gitignore deleted file mode 100644 index a0925114d619..000000000000 --- a/cranelift/fuzz/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target -corpus -artifacts diff --git a/cranelift/fuzz/Cargo.lock b/cranelift/fuzz/Cargo.lock deleted file mode 100644 index 830ec998e5b6..000000000000 --- a/cranelift/fuzz/Cargo.lock +++ /dev/null @@ -1,546 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" - -[[package]] -name = "arbitrary" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237430fd6ed3740afe94eefcc278ae21e050285be882804e0d6e8695f0c94691" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "cc" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cranelift" -version = "0.75.0" -dependencies = [ - "cranelift-codegen", - "cranelift-frontend", -] - -[[package]] -name = "cranelift-bforest" -version = "0.75.0" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.75.0" -dependencies = [ - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-entity", - "gimli", - "log", - "regalloc", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.75.0" -dependencies = [ - "cranelift-codegen-shared", - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.75.0" - -[[package]] -name = "cranelift-entity" -version = "0.75.0" - -[[package]] -name = "cranelift-filetests" -version = "0.73.0" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-frontend", - "cranelift-interpreter", - "cranelift-native", - "cranelift-preopt", - "cranelift-reader", - "file-per-thread-logger", - "filecheck", - "gimli", - "log", - "memmap2", - "num_cpus", - "target-lexicon", - "thiserror", -] - -[[package]] -name = "cranelift-frontend" -version = "0.75.0" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-fuzz" -version = "0.0.0" -dependencies = [ - "arbitrary", - "cranelift", - "cranelift-filetests", - "cranelift-fuzzgen", - "cranelift-interpreter", - "libfuzzer-sys", -] - -[[package]] -name = "cranelift-fuzzgen" -version = "0.75.0" -dependencies = [ - "anyhow", - "arbitrary", - "cranelift", - "rand", -] - -[[package]] -name = "cranelift-interpreter" -version = "0.75.0" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "log", - "smallvec", - "thiserror", -] - -[[package]] -name = "cranelift-native" -version = "0.75.0" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - -[[package]] -name = "cranelift-preopt" -version = "0.75.0" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", -] - -[[package]] -name = "cranelift-reader" -version = "0.75.0" -dependencies = [ - "cranelift-codegen", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "derive_arbitrary" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f1281ee141df08871db9fe261ab5312179eac32d1e314134ceaa8dd7c042f5a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "file-per-thread-logger" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fdbe0d94371f9ce939b555dd342d0686cc4c0cadbcd4b61d70af5ff97eb4126" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "filecheck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe00b427b7c4835f8b82170eb7b9a63634376b63d73b9a9093367e82570bbaa" -dependencies = [ - "regex", - "thiserror", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" -dependencies = [ - "indexmap", -] - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "indexmap" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "libc" -version = "0.2.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" - -[[package]] -name = "memmap2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" -dependencies = [ - "libc", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" - -[[package]] -name = "proc-macro2" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "regalloc" -version = "0.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" -dependencies = [ - "log", - "rustc-hash", - "smallvec", -] - -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "syn" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "target-lexicon" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ae3b39281e4b14b8123bdbaddd472b7dfe215e444181f2f9d2443c2444f834" - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/cranelift/fuzz/Cargo.toml b/cranelift/fuzz/Cargo.toml deleted file mode 100644 index cb58536fe853..000000000000 --- a/cranelift/fuzz/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "cranelift-fuzz" -version = "0.0.0" -authors = ["The Wasmtime Project Developers"] -publish = false -edition = "2018" - -[package.metadata] -cargo-fuzz = true - -[dependencies] -libfuzzer-sys = "0.4" -arbitrary = { version = "1.0.0", features = ["derive"] } - -cranelift = { path = "../umbrella" } -cranelift-filetests = { path = "../filetests" } -cranelift-interpreter = { path = "../interpreter" } -cranelift-fuzzgen = { path = "../fuzzgen" } - -# Prevent this from interfering with workspaces -[workspace] -members = ["."] - -[[bin]] -name = "fuzzgen" -path = "fuzz_targets/fuzzgen.rs" -test = false -doc = false diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index ac46fe8387ae..3b2871032004 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -12,6 +12,9 @@ cargo-fuzz = true cranelift-codegen = { path = "../cranelift/codegen" } cranelift-reader = { path = "../cranelift/reader" } cranelift-wasm = { path = "../cranelift/wasm" } +cranelift-filetests = { path = "../cranelift/filetests" } +cranelift-interpreter = { path = "../cranelift/interpreter" } +cranelift-fuzzgen = { path = "../cranelift/fuzzgen" } libfuzzer-sys = "0.4.0" target-lexicon = "0.12" peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true } @@ -84,3 +87,9 @@ name = "instantiate-maybe-invalid" path = "fuzz_targets/instantiate-maybe-invalid.rs" test = false doc = false + +[[bin]] +name = "cranelift-fuzzgen" +path = "fuzz_targets/cranelift-fuzzgen.rs" +test = false +doc = false diff --git a/cranelift/fuzz/fuzz_targets/fuzzgen.rs b/fuzz/fuzz_targets/cranelift-fuzzgen.rs similarity index 98% rename from cranelift/fuzz/fuzz_targets/fuzzgen.rs rename to fuzz/fuzz_targets/cranelift-fuzzgen.rs index e85b71827722..f37ab244440f 100644 --- a/cranelift/fuzz/fuzz_targets/fuzzgen.rs +++ b/fuzz/fuzz_targets/cranelift-fuzzgen.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; -use cranelift::codegen::data_value::DataValue; +use cranelift_codegen::data_value::DataValue; use cranelift_filetests::function_runner::{CompiledFunction, SingleFunctionCompiler}; use cranelift_fuzzgen::*; use cranelift_interpreter::environment::FuncIndex; From ac03086e3980eb25cf96aea3e79710d173c09ec5 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 12:25:02 +0100 Subject: [PATCH 16/20] cranelift: CLIF-Fuzzer ignore tests that produce traps --- fuzz/fuzz_targets/cranelift-fuzzgen.rs | 39 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/fuzz/fuzz_targets/cranelift-fuzzgen.rs b/fuzz/fuzz_targets/cranelift-fuzzgen.rs index f37ab244440f..2a502ef19890 100644 --- a/fuzz/fuzz_targets/cranelift-fuzzgen.rs +++ b/fuzz/fuzz_targets/cranelift-fuzzgen.rs @@ -11,12 +11,22 @@ use cranelift_interpreter::interpreter::{Interpreter, InterpreterState}; use cranelift_interpreter::step::ControlFlow; use cranelift_interpreter::step::CraneliftTrap; +#[derive(Debug)] enum RunResult { Success(Vec), Trap(CraneliftTrap), Error(Box), } +impl RunResult { + pub fn unwrap(self) -> Vec { + match self { + RunResult::Success(d) => d, + _ => panic!("Expected RunResult::Success in unwrap but got: {:?}", self), + } + } +} + fn run_in_interpreter(interpreter: &mut Interpreter, args: &[DataValue]) -> RunResult { // The entrypoint function is always 0 let index = FuncIndex::from_u32(0); @@ -31,7 +41,6 @@ fn run_in_interpreter(interpreter: &mut Interpreter, args: &[DataValue]) -> RunR } fn run_in_host(compiled_fn: &CompiledFunction, args: &[DataValue]) -> RunResult { - // TODO: What happens if we trap here? let res = compiled_fn.call(args); RunResult::Success(res) } @@ -52,23 +61,25 @@ fuzz_target!(|testcase: TestCase| { for args in &testcase.inputs { let int_res = run_in_interpreter(&mut interpreter, args); - if let RunResult::Error(e) = int_res { - panic!("interpreter failed: {}", e); + match int_res { + RunResult::Success(_) => {} + RunResult::Trap(_) => { + // We currently ignore inputs that trap the interpreter + // We could catch traps in the host run and compare them to the + // interpreter traps, but since we already test trap cases with + // wasm tests and wasm-level fuzzing, the amount of effort does + // not justify implementing it again here. + return; + } + RunResult::Error(_) => panic!("interpreter failed: {:?}", int_res), } let host_res = run_in_host(&compiled_fn, args); - if let RunResult::Error(e) = host_res { - panic!("host failed: {}", e); + match host_res { + RunResult::Success(_) => {} + _ => panic!("host failed: {:?}", host_res), } - match (int_res, host_res) { - (RunResult::Success(lhs), RunResult::Success(rhs)) if lhs == rhs => { - return; - } - (RunResult::Trap(lhs), RunResult::Trap(rhs)) if lhs == rhs => { - return; - } - _ => panic!("Host and Interpreter disagree"), - } + assert_eq!(int_res.unwrap(), host_res.unwrap()); } }); From 0854306a83d1cdad6f991ef08f11cbe1caed7ba1 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 12:49:46 +0100 Subject: [PATCH 17/20] cranelift: CLIF-Fuzzer create new fuzz target to validate generated testcases --- cranelift/fuzzgen/src/lib.rs | 10 +--------- fuzz/Cargo.toml | 6 ++++++ fuzz/fuzz_targets/cranelift-fuzzgen-verify.rs | 11 +++++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 fuzz/fuzz_targets/cranelift-fuzzgen-verify.rs diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 6eade7dcda8c..0fda608be48f 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -3,7 +3,7 @@ use anyhow::Result; use arbitrary::{Arbitrary, Unstructured}; use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::types::*; -use cranelift::codegen::{ir::Function, verify_function}; +use cranelift::codegen::ir::Function; use cranelift::prelude::*; mod function_generator; @@ -41,12 +41,6 @@ where Self { u } } - fn verify_function(&self, func: &Function) -> Result<()> { - let flags = settings::Flags::new(settings::builder()); - verify_function(&func, &flags)?; - Ok(()) - } - fn generate_test_inputs(&mut self, signature: &Signature) -> Result> { // TODO: More test cases? let num_tests = self.u.int_in_range(1..=10)?; @@ -76,8 +70,6 @@ where pub fn generate_test(mut self) -> Result { let func = FunctionGenerator::new(&mut self.u).generate()?; - self.verify_function(&func)?; - let inputs = self.generate_test_inputs(&func.signature)?; Ok(TestCase { func, inputs }) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 3b2871032004..c740016b1386 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -93,3 +93,9 @@ name = "cranelift-fuzzgen" path = "fuzz_targets/cranelift-fuzzgen.rs" test = false doc = false + +[[bin]] +name = "cranelift-fuzzgen-verify" +path = "fuzz_targets/cranelift-fuzzgen-verify.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/cranelift-fuzzgen-verify.rs b/fuzz/fuzz_targets/cranelift-fuzzgen-verify.rs new file mode 100644 index 000000000000..55056bf18c0c --- /dev/null +++ b/fuzz/fuzz_targets/cranelift-fuzzgen-verify.rs @@ -0,0 +1,11 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use cranelift_codegen::{settings, verify_function}; +use cranelift_fuzzgen::TestCase; + +fuzz_target!(|testcase: TestCase| { + let flags = settings::Flags::new(settings::builder()); + verify_function(&testcase.func, &flags).unwrap(); +}); From 1e505c003cd5a9b55844635080b2392e348d6e55 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 13:11:55 +0100 Subject: [PATCH 18/20] cranelift: Store clif-fuzzer config in a separate struct --- cranelift/fuzzgen/src/config.rs | 20 +++++++++++++++++ cranelift/fuzzgen/src/function_generator.rs | 24 +++++++++++++-------- cranelift/fuzzgen/src/lib.rs | 13 +++++++---- 3 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 cranelift/fuzzgen/src/config.rs diff --git a/cranelift/fuzzgen/src/config.rs b/cranelift/fuzzgen/src/config.rs new file mode 100644 index 000000000000..8afb2402492c --- /dev/null +++ b/cranelift/fuzzgen/src/config.rs @@ -0,0 +1,20 @@ +use std::ops::RangeInclusive; + +/// Holds the range of acceptable values to use during the generation of testcases +pub struct Config { + pub test_case_inputs: RangeInclusive, + pub signature_params: RangeInclusive, + pub signature_rets: RangeInclusive, + pub instructions_per_block: RangeInclusive, +} + +impl Default for Config { + fn default() -> Self { + Config { + test_case_inputs: 1..=10, + signature_params: 1..=8, + signature_rets: 1..=8, + instructions_per_block: 1..=16, + } + } +} diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index a0619885d7bc..b3f78a796fc3 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -1,3 +1,4 @@ +use crate::config::Config; use anyhow::Result; use arbitrary::Unstructured; use cranelift::codegen::ir::types::*; @@ -48,7 +49,7 @@ type OpcodeInserter = fn( &'static [Type], ) -> Result<()>; -// TODO: Do we have a way to get this information automatically? +// TODO: Derive this from the `cranelift-meta` generator. const OPCODE_SIGNATURES: &'static [( Opcode, &'static [Type], // Args @@ -88,6 +89,7 @@ where 'data: 'r, { u: &'r mut Unstructured<'data>, + config: &'r Config, vars: Vec<(Type, Variable)>, } @@ -95,8 +97,12 @@ impl<'r, 'data> FunctionGenerator<'r, 'data> where 'data: 'r, { - pub fn new(u: &'r mut Unstructured<'data>) -> Self { - Self { u, vars: vec![] } + pub fn new(u: &'r mut Unstructured<'data>, config: &'r Config) -> Self { + Self { + u, + config, + vars: vec![], + } } fn generate_callconv(&mut self) -> Result { @@ -130,12 +136,11 @@ where let callconv = self.generate_callconv()?; let mut sig = Signature::new(callconv); - // TODO: Unconstrain this - for _ in 0..self.u.int_in_range(0..=8)? { + for _ in 0..self.u.int_in_range(self.config.signature_params.clone())? { sig.params.push(self.generate_abi_param()?); } - for _ in 0..self.u.int_in_range(0..=8)? { + for _ in 0..self.u.int_in_range(self.config.signature_rets.clone())? { sig.returns.push(self.generate_abi_param()?); } @@ -246,7 +251,6 @@ where builder.switch_to_block(block0); builder.seal_block(block0); - // TODO: Cleanup for (i, _) in sig.params.iter().enumerate() { let var = block_vars[i]; let block_param = builder.block_params(block0)[i]; @@ -254,8 +258,10 @@ where let _ = builder.use_var(block_vars[i]); } - // TODO: Unconstrain this - for _ in 0..self.u.int_in_range(0..=16)? { + for _ in 0..self + .u + .int_in_range(self.config.instructions_per_block.clone())? + { self.generate_instruction(&mut builder)?; } diff --git a/cranelift/fuzzgen/src/lib.rs b/cranelift/fuzzgen/src/lib.rs index 0fda608be48f..ae1b3bb6bb6d 100644 --- a/cranelift/fuzzgen/src/lib.rs +++ b/cranelift/fuzzgen/src/lib.rs @@ -1,3 +1,4 @@ +use crate::config::Config; use crate::function_generator::FunctionGenerator; use anyhow::Result; use arbitrary::{Arbitrary, Unstructured}; @@ -6,6 +7,7 @@ use cranelift::codegen::ir::types::*; use cranelift::codegen::ir::Function; use cranelift::prelude::*; +mod config; mod function_generator; pub type TestCaseInput = Vec; @@ -31,6 +33,7 @@ where 'data: 'r, { u: &'r mut Unstructured<'data>, + config: Config, } impl<'r, 'data> FuzzGen<'r, 'data> @@ -38,12 +41,14 @@ where 'data: 'r, { pub fn new(u: &'r mut Unstructured<'data>) -> Self { - Self { u } + Self { + u, + config: Config::default(), + } } fn generate_test_inputs(&mut self, signature: &Signature) -> Result> { - // TODO: More test cases? - let num_tests = self.u.int_in_range(1..=10)?; + let num_tests = self.u.int_in_range(self.config.test_case_inputs.clone())?; let mut inputs = Vec::with_capacity(num_tests); for _ in 0..num_tests { @@ -69,7 +74,7 @@ where } pub fn generate_test(mut self) -> Result { - let func = FunctionGenerator::new(&mut self.u).generate()?; + let func = FunctionGenerator::new(&mut self.u, &self.config).generate()?; let inputs = self.generate_test_inputs(&func.signature)?; Ok(TestCase { func, inputs }) From c808f09f88b7e78c95f0c36c3c9ca283cd3dda7c Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Tue, 29 Jun 2021 16:59:10 +0100 Subject: [PATCH 19/20] cranelift: Generate variables upfront per function --- cranelift/fuzzgen/src/config.rs | 10 ++- cranelift/fuzzgen/src/function_generator.rs | 98 ++++++++------------- 2 files changed, 45 insertions(+), 63 deletions(-) diff --git a/cranelift/fuzzgen/src/config.rs b/cranelift/fuzzgen/src/config.rs index 8afb2402492c..cb10ae877ef4 100644 --- a/cranelift/fuzzgen/src/config.rs +++ b/cranelift/fuzzgen/src/config.rs @@ -6,15 +6,19 @@ pub struct Config { pub signature_params: RangeInclusive, pub signature_rets: RangeInclusive, pub instructions_per_block: RangeInclusive, + /// Number of variables that we allocate per function + /// This value does not include the signature params + pub vars_per_function: RangeInclusive, } impl Default for Config { fn default() -> Self { Config { test_case_inputs: 1..=10, - signature_params: 1..=8, - signature_rets: 1..=8, - instructions_per_block: 1..=16, + signature_params: 0..=16, + signature_rets: 0..=16, + instructions_per_block: 0..=64, + vars_per_function: 0..=16, } } } diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs index b3f78a796fc3..a92b71eae1c6 100644 --- a/cranelift/fuzzgen/src/function_generator.rs +++ b/cranelift/fuzzgen/src/function_generator.rs @@ -2,7 +2,7 @@ use crate::config::Config; use anyhow::Result; use arbitrary::Unstructured; use cranelift::codegen::ir::types::*; -use cranelift::codegen::ir::{AbiParam, ExternalName, Function, Opcode, Signature, Type}; +use cranelift::codegen::ir::{AbiParam, ExternalName, Function, Opcode, Signature, Type, Value}; use cranelift::codegen::isa::CallConv; use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; use cranelift::prelude::{EntityRef, InstBuilder}; @@ -25,19 +25,20 @@ fn insert_opcode_arity_2( args: &'static [Type], rets: &'static [Type], ) -> Result<()> { - let arg0 = fgen.get_variable_of_type(builder, args[0])?; + let arg0 = fgen.get_variable_of_type(args[0])?; let arg0 = builder.use_var(arg0); - let arg1 = fgen.get_variable_of_type(builder, args[1])?; + let arg1 = fgen.get_variable_of_type(args[1])?; let arg1 = builder.use_var(arg1); let typevar = rets[0]; let (inst, dfg) = builder.ins().Binary(opcode, typevar, arg0, arg1); + let results = dfg.inst_results(inst).to_vec(); - let val = dfg.first_result(inst); - let var = fgen.create_var(builder, typevar)?; - builder.def_var(var, val); - + for (val, ty) in results.into_iter().zip(rets) { + let var = fgen.get_variable_of_type(*ty)?; + builder.def_var(var, val); + } Ok(()) } @@ -164,50 +165,25 @@ where .collect() } - /// Get a variable of type `ty`, either reusing a old var, or generating a new one with a - /// `iconst`/`fconst`. - fn get_variable_of_type( - &mut self, - builder: &mut FunctionBuilder, - ty: Type, - ) -> Result { - // TODO: global vars - - let mut opts: Vec< - fn( - fgen: &mut FunctionGenerator, - builder: &mut FunctionBuilder, - ty: Type, - ) -> Result, - > = Vec::with_capacity(2); - - // Generate new - opts.push(|fgen, builder, ty| { - let imm64 = match ty { - I8 => fgen.u.arbitrary::()? as i64, - I16 => fgen.u.arbitrary::()? as i64, - I32 => fgen.u.arbitrary::()? as i64, - I64 => fgen.u.arbitrary::()?, - _ => unreachable!(), - }; - let var = fgen.create_var(builder, ty)?; - let val = builder.ins().iconst(ty, imm64); - builder.def_var(var, val); - - Ok(var) - }); - - // Reuse var - if self.vars_of_type(ty).len() != 0 { - opts.push(|fg, _, ty| { - let opts = fg.vars_of_type(ty); - let var = fg.u.choose(&opts[..])?; - Ok(*var) - }); - } + /// Get a variable of type `ty` from the current function + fn get_variable_of_type(&mut self, ty: Type) -> Result { + let opts = self.vars_of_type(ty); + let var = self.u.choose(&opts[..])?; + Ok(*var) + } - let f = self.u.choose(&opts[..])?; - f(self, builder, ty) + /// Generates an instruction(`iconst`/`fconst`/etc...) to introduce a constant value + fn generate_const(&mut self, builder: &mut FunctionBuilder, ty: Type) -> Result { + let imm64 = match ty { + I8 => self.u.arbitrary::()? as i64, + I16 => self.u.arbitrary::()? as i64, + I32 => self.u.arbitrary::()? as i64, + I64 => self.u.arbitrary::()?, + _ => unreachable!(), + }; + let val = builder.ins().iconst(ty, imm64); + + Ok(val) } fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> { @@ -215,7 +191,7 @@ where let vars = ret_params .iter() - .map(|p| self.get_variable_of_type(builder, p.value_type)) + .map(|p| self.get_variable_of_type(p.value_type)) .collect::>>()?; let vals = vars @@ -241,21 +217,23 @@ where let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx); let block0 = builder.create_block(); - - let mut block_vars = vec![]; - for param in &sig.params { - let var = self.create_var(&mut builder, param.value_type)?; - block_vars.push(var); - } builder.append_block_params_for_function_params(block0); builder.switch_to_block(block0); builder.seal_block(block0); - for (i, _) in sig.params.iter().enumerate() { - let var = block_vars[i]; + // Define variables for the function signature + for (i, param) in sig.params.iter().enumerate() { + let var = self.create_var(&mut builder, param.value_type)?; let block_param = builder.block_params(block0)[i]; builder.def_var(var, block_param); - let _ = builder.use_var(block_vars[i]); + } + + // Create a pool of vars that are going to be used in this function + for _ in 0..self.u.int_in_range(self.config.vars_per_function.clone())? { + let ty = self.generate_type()?; + let var = self.create_var(&mut builder, ty)?; + let value = self.generate_const(&mut builder, ty)?; + builder.def_var(var, value); } for _ in 0..self From 15ee4fe6874dd0d8bf683366239c90db973c091e Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Thu, 1 Jul 2021 10:42:31 +0100 Subject: [PATCH 20/20] cranelift: Prevent publishing of fuzzgen crate --- cranelift/fuzzgen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cranelift/fuzzgen/Cargo.toml b/cranelift/fuzzgen/Cargo.toml index 2ad85cd7b425..aa37a74e0b6a 100644 --- a/cranelift/fuzzgen/Cargo.toml +++ b/cranelift/fuzzgen/Cargo.toml @@ -7,7 +7,7 @@ license = "Apache-2.0 WITH LLVM-exception" repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition = "2018" -publish=false +publish = false [dependencies]