Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for dumping variables using 'printf' for testing scenarios where std is not avalible. #3

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b15c412
Draft version of formatting using printf(does not support enums yet, …
FractalFir Jan 31, 2024
cd79ada
Printf dumper is now toglable from the command line, and can display …
FractalFir Jan 31, 2024
293bc43
Added support for formatting larger tuples.
FractalFir Jan 31, 2024
864d04b
Cleanup, comments, small fixes for large tuples
FractalFir Jan 31, 2024
7119f94
Added implementations of the formatting trait for even larger tuples
FractalFir Jan 31, 2024
1dc3061
Fixed a small issue with array formatting
FractalFir Jan 31, 2024
345ce26
made debug_printf an unsafe function. Implemented the PrintFDebug tra…
FractalFir Jan 31, 2024
97ce8ec
Small fix to generated printf formatting
FractalFir Feb 5, 2024
6d60354
Small fix to generated printf formatting
FractalFir Feb 5, 2024
120bf6d
Small fix to 128 bit int formatting
FractalFir Feb 12, 2024
38e581b
Fixed a couple of typos
FractalFir Feb 12, 2024
dc8c0ff
Merge branch 'cbeuw:master' into master
FractalFir Feb 18, 2024
25ec83d
Fixed missing null terminators in enum display
FractalFir Apr 28, 2024
eaf5b10
Fixed printing of NaN values using printf printing the sign of a NaN.…
FractalFir May 11, 2024
c843fb6
Small fix
FractalFir May 11, 2024
350b556
Update CITATION.cff
cbeuw Oct 14, 2024
644e14b
Merge branch 'master' into master
FractalFir Nov 12, 2024
8183b5a
FIxed issues with dumper caused by the merge.
FractalFir Nov 12, 2024
87cb1c4
Some initial work on generating source files compatible with rust-gpu
FractalFir Dec 1, 2024
b3726ac
Misc fixes to the rustgpu support.
FractalFir Dec 1, 2024
e47e1cc
Fixes to formatting code for 'rust-gpu'
FractalFir Dec 2, 2024
bdce6d4
Started working on supporting disabling fuzzing enums and 128 bit int…
FractalFir Dec 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
cff-version: 1.2.0
title: Rustlantis
message: A Rust Mid-level Intermediate Representation fuzzer
message: A fuzzer for the Rust compiler
type: software
authors:
- given-names: Andy
- given-names: Qian (Andy)
family-names: Wang
affiliation: ETH Zürich
orcid: 'https://orcid.org/0009-0006-0779-8651'
affiliation: ETH Zürich
- given-names: Ralf
family-names: Jung
affiliation: ETH Zürich
orcid: 'https://orcid.org/0000-0001-7669-6348'
affiliation: ETH Zürich
identifiers:
- type: url
value: >-
https://ethz.ch/content/dam/ethz/special-interest/infk/inst-pls/plf-dam/documents/StudentProjects/MasterTheses/2023-Andy-Thesis.pdf
- type: doi
value: 10.1145/3689780
repository-code: 'https://github.com/cbeuw/rustlantis'
abstract: >-
Rustlantis is a novel fuzzer capable of generating
programs in Rust’s Mid-level Intermediate Representation
that are deterministic and free from Undefined Behaviour.
keywords:
- Compiler testing
- Rust
- fuzzing
- software validation
- compilers
- Fuzzing
license:
- Apache-2.0
- MIT
- Apache-2.0
- MIT
5 changes: 3 additions & 2 deletions difftest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ impl ExecResults {
'outer: for (&name, result) in map {
for (class_result, names) in &mut eq_classes {
// Put into an existing equivalence class
let eq = if let Ok(class_out) = class_result && let Ok(out) = result {
let eq = if let Ok(class_out) = class_result
&& let Ok(out) = result
{
class_out.stdout == out.stdout
} else {
result == class_result

};
if eq {
names.insert(name);
Expand Down
4 changes: 3 additions & 1 deletion generate/src/generation/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ impl CoreIntrinsic for Transmute {
fn dest_type(&self, ty: TyId, tcx: &TyCtxt) -> bool {
if ty.contains(tcx, |tcx, ty| match ty.kind(tcx) {
// Tys with value validity contstraints
TyKind::Unit | TyKind::Bool | TyKind::Char | TyKind::RawPtr(..) | TyKind::Ref(..) => true, // TODO: pointer transmute
TyKind::Unit | TyKind::Bool | TyKind::Char | TyKind::RawPtr(..) | TyKind::Ref(..) => {
true
} // TODO: pointer transmute
_ => false,
}) {
return false;
Expand Down
147 changes: 109 additions & 38 deletions generate/src/generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use mir::syntax::{
SwitchTargets, Terminator, TyId, TyKind, UnOp, VariantIdx,
};
use mir::tyctxt::TyCtxt;
use mir::VarDumper;
use rand::seq::SliceRandom;
use rand::{seq::IteratorRandom, Rng, RngCore, SeedableRng};
use rand_distr::{Distribution, WeightedError, WeightedIndex};
Expand Down Expand Up @@ -300,40 +301,80 @@ impl GenerationCtx {
let target_ty = lhs.ty(self.current_decls(), &self.tcx);
let source_tys = match target_ty.kind(&self.tcx) {
// TODO: no int to ptr cast for now
TyKind::Int(..) | TyKind::Uint(..) => &[
TyCtxt::ISIZE,
TyCtxt::I8,
TyCtxt::I16,
TyCtxt::I32,
TyCtxt::I64,
TyCtxt::I128,
TyCtxt::USIZE,
TyCtxt::U8,
TyCtxt::U16,
TyCtxt::U32,
TyCtxt::U64,
TyCtxt::U128,
TyCtxt::F32,
TyCtxt::F64,
TyCtxt::CHAR,
TyCtxt::BOOL,
][..],
TyKind::Float(..) => &[
TyCtxt::ISIZE,
TyCtxt::I8,
TyCtxt::I16,
TyCtxt::I32,
TyCtxt::I64,
TyCtxt::I128,
TyCtxt::USIZE,
TyCtxt::U8,
TyCtxt::U16,
TyCtxt::U32,
TyCtxt::U64,
TyCtxt::U128,
TyCtxt::F32,
TyCtxt::F64,
][..],
TyKind::Int(..) | TyKind::Uint(..) => {
if self.tcx.no_128_bit_ints() {
&[
TyCtxt::ISIZE,
TyCtxt::I8,
TyCtxt::I16,
TyCtxt::I32,
TyCtxt::I64,
TyCtxt::USIZE,
TyCtxt::U8,
TyCtxt::U16,
TyCtxt::U32,
TyCtxt::U64,
TyCtxt::F32,
TyCtxt::F64,
TyCtxt::CHAR,
TyCtxt::BOOL,
][..]
} else {
&[
TyCtxt::ISIZE,
TyCtxt::I8,
TyCtxt::I16,
TyCtxt::I32,
TyCtxt::I64,
TyCtxt::I128,
TyCtxt::USIZE,
TyCtxt::U8,
TyCtxt::U16,
TyCtxt::U32,
TyCtxt::U64,
TyCtxt::U128,
TyCtxt::F32,
TyCtxt::F64,
TyCtxt::CHAR,
TyCtxt::BOOL,
][..]
}
}
TyKind::Float(..) => {
if self.tcx.no_128_bit_ints() {
&[
TyCtxt::ISIZE,
TyCtxt::I8,
TyCtxt::I16,
TyCtxt::I32,
TyCtxt::I64,
TyCtxt::USIZE,
TyCtxt::U8,
TyCtxt::U16,
TyCtxt::U32,
TyCtxt::U64,
TyCtxt::F32,
TyCtxt::F64,
][..]
} else {
&[
TyCtxt::ISIZE,
TyCtxt::I8,
TyCtxt::I16,
TyCtxt::I32,
TyCtxt::I64,
TyCtxt::I128,
TyCtxt::USIZE,
TyCtxt::U8,
TyCtxt::U16,
TyCtxt::U32,
TyCtxt::U64,
TyCtxt::U128,
TyCtxt::F32,
TyCtxt::F64,
][..]
}
}
_ => &[][..],
};
let rvalue = self.make_choice(
Expand Down Expand Up @@ -990,7 +1031,10 @@ impl GenerationCtx {
for vars in dumpped.chunks(Program::DUMPER_ARITY) {
let new_bb = self.add_new_bb();

let args = if self.program.use_debug_dumper {
let args = if matches!(
self.program.var_dumper,
VarDumper::StdVarDumper | VarDumper::PrintfVarDumper { .. }
) {
let mut args = Vec::with_capacity(1 + Program::DUMPER_ARITY * 2);
args.push(Operand::Constant(
self.cursor.function.index().try_into().unwrap(),
Expand Down Expand Up @@ -1182,9 +1226,36 @@ impl GenerationCtx {
}
}

pub fn new(seed: u64, debug_dump: bool) -> Self {
fn make_choice_mut<T, F, R>(
&mut self,
choices: impl Iterator<Item = T> + Clone,
mut use_choice: F,
) -> Result<R>
where
F: FnMut(&mut Self, T) -> Result<R>,
T: Clone,
{
let mut failed: Vec<usize> = vec![];
loop {
let (i, choice) = choices
.clone()
.enumerate()
.filter(|(i, _)| !failed.contains(i))
.choose(&mut *self.rng.borrow_mut())
.ok_or(SelectionError::Exhausted)?;
let res = use_choice(self, choice.clone());
match res {
Ok(val) => return Ok(val),
Err(_) => {
failed.push(i);
}
}
}
}

pub fn new(seed: u64, debug_dump: VarDumper, no_enums: bool,no_128_bit_ints:bool) -> Self {
let rng = RefCell::new(Box::new(rand::rngs::SmallRng::seed_from_u64(seed)));
let tcx = Rc::new(seed_tys(&mut *rng.borrow_mut()));
let tcx = Rc::new(seed_tys(&mut *rng.borrow_mut(), no_enums,no_128_bit_ints));
let ty_weights = TySelect::new(&tcx);
// TODO: don't zero-initialize current_function and current_bb
Self {
Expand Down Expand Up @@ -1238,7 +1309,7 @@ impl GenerationCtx {
let arg_tys: Vec<TyId> = self
.tcx
.indices()
.filter(|ty| <dyn RngCore>::is_literalble(*ty, &self.tcx))
.filter(|ty| <dyn RngCore>::is_literalble(*ty, &self.tcx) )
.choose_multiple(&mut *self.rng.borrow_mut(), args_count);
let arg_literals: Vec<Literal> = arg_tys
.iter()
Expand Down
13 changes: 10 additions & 3 deletions generate/src/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,17 @@ impl Distribution<isize> for Sombrero {

pub trait GenLiteral: Rng {
fn is_literalble(ty: TyId, tcx: &TyCtxt) -> bool {
match ty.kind(tcx) {
let res = match ty.kind(tcx) {
TyKind::Unit => false,
_ => ty.is_scalar(tcx),
}
_ => {
if tcx.no_128_bit_ints() && (ty == TyCtxt::U128 || ty == TyCtxt::I128) {
false
} else {
ty.is_scalar(tcx)
}
}
};
res
}
fn gen_literal(&mut self, ty: TyId, tcx: &TyCtxt) -> Option<Literal> {
let lit: Literal = match ty.kind(tcx) {
Expand Down
40 changes: 36 additions & 4 deletions generate/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
mod generation;
mod literal;
mod mem;
mod place_select;
mod pgraph;
mod place_select;
mod ty;

use mir::VarDumper;
use std::time::Instant;

use clap::{arg, command, value_parser, Arg};
Expand All @@ -26,11 +26,18 @@ fn main() {
let matches = command!()
.args(&[
arg!(-d --debug "generate a program where values are printed instead of hashed (slow)"),

arg!(-p --printf_debug "generate a program where values are printed using the C 'printf' function instead of hashed (slow)"),
arg!(--rust_gpu "generate a program where values are printed using the C 'printf' function instead of hashed (slow)"),
arg!(--no_enums "generate a program without enums"),
arg!(--no_128_bit_ints "generate a program without 128 bit ints"),

Arg::new("call-syntax")
.long("call-syntax")
.value_parser(["v1", "v2", "v3", "v4"])
.default_value("v4")
.help("switch between different versions of Call syntaxes"),

arg!(<seed> "generation seed").value_parser(value_parser!(u64)),
])
.get_matches();
Expand All @@ -39,13 +46,38 @@ fn main() {
.get_one::<u64>("seed")
.expect("need an integer as seed");
let debug_dump = matches.get_one::<bool>("debug").copied().unwrap_or(false);
let rust_gpu = matches
.get_one::<bool>("rust_gpu")
.copied()
.unwrap_or(false);
let no_enums = matches
.get_one::<bool>("no_enums")
.copied()
.unwrap_or(false);
let no_128_bit_ints = matches
.get_one::<bool>("no_128_bit_ints")
.copied()
.unwrap_or(false);
let printf_dump = matches
.get_one::<bool>("printf_debug")
.copied()
.unwrap_or(false)
| rust_gpu;
let dumper = match (debug_dump,printf_dump){
(false,false)=>VarDumper::HashDumper,
(true,false)=>VarDumper::StdVarDumper,
(false,true)=>VarDumper::PrintfVarDumper{rust_gpu},
(true,true)=>panic!("You can only choose either the `debug` dumper or `printf_debug` dumper, but both of them have been selected."),
};
info!("Generating a program with seed {seed}");

let call_syntax = matches.get_one::<String>("call-syntax").unwrap();
let genctxt = GenerationCtx::new(seed, debug_dump);
let genctxt = GenerationCtx::new(seed, dumper, no_enums,no_128_bit_ints);
let time = Instant::now();
let (program, tcx) = genctxt.generate();
println!("{}", program.serialize(&tcx, call_syntax.as_str().into()));
println!("{}", tcx.serialize());
println!("{}", tcx.serialize(dumper));

let dur = time.elapsed();
debug!("took {}s to generate", dur.as_secs_f32());
}
3 changes: 3 additions & 0 deletions generate/src/place_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ impl PlaceSelector {
}

fn into_iter_path(self, pt: &PlaceGraph) -> impl Iterator<Item = PlacePath> + Clone + '_ {

let exclusion_indicies: Vec<PlaceIndex> = self
.exclusions
.iter()
Expand Down Expand Up @@ -289,10 +290,12 @@ impl PlaceSelector {
pub fn into_weighted(self, pt: &PlaceGraph) -> Option<(Vec<PlacePath>, WeightedIndex<Weight>)> {
let usage = self.usage;
let tcx = self.tcx.clone();

let (places, weights): (Vec<PlacePath>, Vec<Weight>) =
self.into_iter_path(pt)
.map(|ppath| {
let place = ppath.target_index();

let mut weight = match usage {
PlaceUsage::Argument => {
let mut weight = 1;
Expand Down
Loading