Skip to content

Fuzzing #655

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

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/comp/syntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export make_fold;
export dummy_out;
export noop_fold_crate;
export noop_fold_item;
export noop_fold_expr;

type ast_fold = @mutable a_f;

Expand Down
1 change: 1 addition & 0 deletions src/comp/syntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ fn print_item(&ps s, &@ast::item item) {
case (ast::item_native_mod(?nmod)) {
head(s, "native");
alt (nmod.abi) {
case (ast::native_abi_llvm) { word_nbsp(s, "\"llvm\""); }
case (ast::native_abi_rust) { word_nbsp(s, "\"rust\""); }
case (ast::native_abi_cdecl) { word_nbsp(s, "\"cdecl\""); }
case (ast::native_abi_rust_intrinsic) {
Expand Down
32 changes: 32 additions & 0 deletions src/fuzzer/ast_match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std;
import std::ivec;

fn ivec_equal[T](&T[] v, &T[] u, fn (&T, &T) -> bool element_equality_test) -> bool {
auto Lv = ivec::len(v);
if (Lv != ivec::len(u)) {
ret false;
}
auto i = 0u;
while (i < Lv) {
if (!element_equality_test(v.(i), u.(i))) {
ret false;
}
i += 1u;
}
ret true;
}

fn builtin_equal[T](&T a, &T b) -> bool {
ret a == b;
}

fn main() {
assert builtin_equal(5, 5);
assert !builtin_equal(5, 4);
assert !ivec_equal(~[5, 5], ~[5], builtin_equal);
assert !ivec_equal(~[5, 5], ~[5, 4], builtin_equal);
assert !ivec_equal(~[5, 5], ~[4, 5], builtin_equal);
assert ivec_equal(~[5, 5], ~[5, 5], builtin_equal);

log_err "Pass";
}
167 changes: 134 additions & 33 deletions src/fuzzer/fuzzer.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,161 @@
use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::io;
import std::io::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;

type src_gen = iter() -> str;
import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::print::pprust;

iter dir_src_gen(str dir) -> str {
}
import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

fn usage(str binary) {
io::stdout().write_line("usage");
}
/*
// Imports for "the rest of driver::compile_input"
import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

type session = rec(str srcdir);

fn make_session(vec[str] args) -> session {
// Directory of rust source files to use as input
auto opt_src = "src";
fn file_contains(&str filename, &str needle) -> bool {
auto r = io::file_reader(filename);
auto contents = str::unsafe_from_bytes(r.read_whole_stream());
ret str::find(contents, needle) != -1;
}

auto binary = vec::shift[str](args);
auto opts = [optopt(opt_src)];
auto match;
alt (getopts::getopts(args, opts)) {
case (getopts::failure(?f)) {
log_err #fmt("error: %s", getopts::fail_str(f));
fail;
fn find_rust_files(&mutable str[] files, str path) {
if (str::ends_with(path, ".rs")) {
if (file_contains(path, "xfail-stage1")) {
//log_err "Skipping " + path + " because it is marked as xfail-stage1";
} else if (
!str::ends_with(path, "constrained-type.rs") && // https://github.com/graydon/rust/issues/653
str::find(path, "utf8") != -1 && // https://github.com/graydon/rust/issues/654
true) {
//log_err "Skipping " + path + " because of a known bug";
} else {
files += ~[path];
}
case (getopts::success(?m)) {
match = m;
} else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1) {
for (str p in fs::list_dir(path)) {
find_rust_files(files, p);
}
};

if (!opt_present(match, opt_src)) {
usage(binary);
fail;
}
}

auto srcdir = opt_str(match, opt_src);
fn steal_exprs(&ast::crate crate) -> ast::expr[] {
let @mutable ast::expr[] exprs = @mutable ~[];
// "Stash" cannot be type-parameterized because of https://github.com/graydon/rust/issues/375
fn stash_expr(@mutable ast::expr[] es, &@ast::expr e) { *es += ~[*e]; }
auto v = rec(visit_expr_pre = bind stash_expr(exprs, _) with walk::default_visitor());
walk::walk_crate(v, crate);
*exprs
}

ret rec(srcdir = srcdir);
// https://github.com/graydon/rust/issues/652
fn safe_to_replace(ast::expr_ e) -> bool {
alt (e) {
case (ast::expr_if(_, _, _)) { false }
case (ast::expr_block(_)) { false }
case (_) { true }
}
}

fn log_session(session sess) {
log #fmt("srcdir: %s", sess.srcdir);
// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(&ast::crate crate, uint i, ast::expr_ newexpr) -> ast::crate {
let @mutable uint j = @mutable 0u;
fn fold_expr_rep(@mutable uint j_, uint i_, &ast::expr_ newexpr_, &ast::expr_ original, fold::ast_fold fld) -> ast::expr_ {
*j_ += 1u;
if (i_ + 1u == *j_ && safe_to_replace(original)) {
newexpr_
} else {
fold::noop_fold_expr(original, fld)
}
}
auto afp = rec(fold_expr = bind fold_expr_rep(j, i, newexpr, _, _) with *fold::default_ast_fold());
auto af = fold::make_fold(afp);
let @ast::crate crate2 = @af.fold_crate(crate);
fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
*crate2
}

fn run_session(session sess) {
iter under(uint n) -> uint { let uint i = 0u; while (i < n) { put i; i += 1u; } }

fn devnull() -> io::writer { std::io::string_writer().get_writer() }

fn pp_variants(&ast::crate crate, &session::session sess, &str filename) {
auto exprs = steal_exprs(crate);
auto exprsL = ivec::len(exprs);
if (exprsL < 100u) {
for each (uint i in under(uint::min(exprsL, 20u))) {
log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
for each (uint j in under(uint::min(exprsL, 5u))) {
log_err "With... " + pprust::expr_to_str(@exprs.(j));
auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
pprust::print_crate(sess.get_codemap(), crate2, filename, devnull(), pprust::no_ann());
}
}
}
}

fn main(vec[str] args) {
auto sess = make_session(args);
log_session(sess);
run_session(sess);
auto files = ~[];
auto root = "/Users/jruderman/code/rust/src/"; // XXX
find_rust_files(files, root); // not using time here because that currently screws with passing-a-mutable-array
log_err uint::str(ivec::len(files)) + " files";

auto binary = vec::shift[str](args);
auto binary_dir = fs::dirname(binary);

let @session::options sopts =
@rec(library=false,
static=false,
optimize=0u,
debuginfo=false,
verify=true,
run_typestate=true,
save_temps=false,
stats=false,
time_passes=false,
time_llvm_passes=false,
output_type=link::output_type_bitcode,
library_search_paths=[binary_dir + "/lib"],
sysroot=driver::get_default_sysroot(binary),
cfg=~[],
test=false);

for (str file in files) {
log_err "=== " + file + " ===";
let session::session sess = driver::build_session(sopts);
let @ast::crate crate = time(true, "parsing " + file, bind driver::parse_input(sess, ~[], file));
pprust::print_crate(sess.get_codemap(), crate, file, devnull(), pprust::no_ann());
pp_variants(*crate, sess, file);
}
}

// Local Variables:
Expand Down
98 changes: 98 additions & 0 deletions src/fuzzer/ivec_fuzz.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*

Idea: provide functions for 'exhaustive' and 'random' modification of vecs.

two functions, "return all edits" and "return a random edit" <-- leaning toward this model
or
two functions, "return the number of possible edits" and "return edit #n"

It would be nice if this could be data-driven, so the two functions could share information:
type vec_modifier = rec(fn (&T[] v, uint i) -> T[] fun, uint lo, uint di);
const vec_modifier[] vec_modifiers = ~[rec(fun=vec_omit, 0u, 1u), ...];
But that gives me "error: internal compiler error unimplemented consts that's not a plain literal".
https://github.com/graydon/rust/issues/570

vec_edits is not an iter because iters might go away and:
https://github.com/graydon/rust/issues/639

vec_omit and friends are not type-parameterized because:
https://github.com/graydon/rust/issues/640

*/

use std;
import std::ivec;
import std::ivec::slice;
import std::ivec::len;
import std::int;

//fn vec_reverse(&T[] v) -> T[] { ... }

fn vec_omit [T] (&T[] v, uint i) -> T[] { slice(v, 0u, i) + slice(v, i+1u, len(v)) }
fn vec_dup [T] (&T[] v, uint i) -> T[] { slice(v, 0u, i) + ~[v.(i)] + slice(v, i, len(v)) }
fn vec_swadj [T] (&T[] v, uint i) -> T[] { slice(v, 0u, i) + ~[v.(i+1u), v.(i)] + slice(v, i+2u, len(v)) }
fn vec_prefix [T] (&T[] v, uint i) -> T[] { slice(v, 0u, i) }
fn vec_suffix [T] (&T[] v, uint i) -> T[] { slice(v, i, len(v)) }

fn vec_poke [T] (&T[] v, uint i, &T x) -> T[] { slice(v, 0u, i) + ~[x] + slice(v, i+1u, len(v)) }
fn vec_insert [T] (&T[] v, uint i, &T x) -> T[] { slice(v, 0u, i) + ~[x] + slice(v, i, len(v)) }

// Iterates over 0...length, skipping the specified number on each side.
iter ix(uint skip_low, uint skip_high, uint length) -> uint { let uint i = skip_low; while (i + skip_high <= length) { put i; i += 1u; } }

// Returns a bunch of modified versions of v, some of which introduce new elements (borrowed from xs).
fn vec_edits[T](&T[] v, &T[] xs) -> T[][] {
let T[][] edits = ~[];
let uint Lv = len(v);

if (Lv != 1u) { edits += ~[~[]]; } // When Lv == 1u, this is redundant with omit
//if (Lv >= 3u) { edits += ~[vec_reverse(v)]; }

for each (uint i in ix(0u, 1u, Lv)) { edits += ~[vec_omit (v, i)]; }
for each (uint i in ix(0u, 1u, Lv)) { edits += ~[vec_dup (v, i)]; }
for each (uint i in ix(0u, 2u, Lv)) { edits += ~[vec_swadj (v, i)]; }
for each (uint i in ix(1u, 2u, Lv)) { edits += ~[vec_prefix(v, i)]; }
for each (uint i in ix(2u, 1u, Lv)) { edits += ~[vec_suffix(v, i)]; }

for each (uint j in ix(0u, 1u, len(xs))) {
for each (uint i in ix(0u, 1u, Lv)) { edits += ~[vec_poke (v, i, xs.(j))]; }
for each (uint i in ix(0u, 0u, Lv)) { edits += ~[vec_insert(v, i, xs.(j))]; }
}

edits
}

// Would be nice if this were built in: https://github.com/graydon/rust/issues/424
fn vec_to_str(&int[] v) -> str {
auto i = 0u;
auto s = "[";
while (i < len(v)) {
s += int::str(v.(i));
if (i + 1u < len(v)) {
s += ", "
}
i += 1u;
}
ret s + "]";
}

fn show_edits(&int[] a, &int[] xs) {
log_err "=== Edits of " + vec_to_str(a) + " ===";
auto b = vec_edits(a, xs);
for each (uint i in ix(0u, 1u, len(b))) {
log_err vec_to_str(b.(i));
}
}

fn demo_edits() {
auto xs = ~[7, 8];
show_edits(~[], xs);
show_edits(~[1], xs);
show_edits(~[1,2], xs);
show_edits(~[1,2,3], xs);
show_edits(~[1,2,3,4], xs);
}

fn main() {
demo_edits();
}
2 changes: 2 additions & 0 deletions src/lib/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pred gt(uint x, uint y) -> bool { ret x > y; }

fn max(uint x, uint y) -> uint { if (x > y) { ret x; } ret y; }

fn min(uint x, uint y) -> uint { if (x > y) { ret y; } ret x; }

iter range(uint lo, uint hi) -> uint {
auto lo_ = lo;
while (lo_ < hi) { put lo_; lo_ += 1u; }
Expand Down