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

Create a structure-aware JavaScript fuzzer to find deep bugs #1902

Closed
wants to merge 40 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
95c8623
init fuzzer
addisoncrump Mar 6, 2022
bba1252
appease the mighty rustfmt
addisoncrump Mar 6, 2022
6134df0
fix clippy errors by simply allowing it!
addisoncrump Mar 6, 2022
1550ce4
fix clippy + build by using spin and iter_mut
addisoncrump Mar 6, 2022
b7c7caa
simplify fuzzer input generation
addisoncrump Mar 6, 2022
0e5ad66
fix compile errors when not in feature fuzzer
addisoncrump Mar 6, 2022
c6ed17b
add missing cfg
addisoncrump Mar 6, 2022
377f55f
better clippy lint control
addisoncrump Mar 6, 2022
0d500b9
standardise arbitrary impl for Name
addisoncrump Mar 6, 2022
e56192c
final touches
addisoncrump Mar 6, 2022
00cbbb1
fix issue caused by a previous bad merge
addisoncrump Mar 6, 2022
4b17781
update deps for inputgen
addisoncrump Mar 6, 2022
53ca1a6
Merge branch 'main' of github.com:boa-dev/boa into experimental-fuzzer
addisoncrump Mar 6, 2022
186be92
Merge branch 'main' of github.com:boa-dev/boa into experimental-fuzzer
addisoncrump Mar 8, 2022
ea9540b
fix various updates w.r.t. assigntarget
addisoncrump Mar 8, 2022
621838f
fix some of the tree walking (string disappeared fixes)
addisoncrump Mar 8, 2022
ca9d0ab
Merge branch 'main' of github.com:boa-dev/boa into experimental-fuzzer
addisoncrump Mar 12, 2022
30dc27c
explicitly handle this and empty in case new cases are introduced later
addisoncrump Mar 12, 2022
4b65c4d
remove the pesky comma preventing rustfmt from succeeding
addisoncrump Mar 12, 2022
1ee7813
Merge branch 'main' of github.com:boa-dev/boa into experimental-fuzzer
addisoncrump Mar 14, 2022
4f82142
mergeup
addisoncrump Mar 17, 2022
d38fef5
update cargo information to be consistent with others
addisoncrump Mar 17, 2022
da91d86
split inputgen
addisoncrump Mar 17, 2022
6abaa1b
explain max_insns
addisoncrump Mar 17, 2022
750d166
docs
addisoncrump Mar 17, 2022
c1bd554
whoops, remove testing file
addisoncrump Mar 17, 2022
990740b
whoops, missed a docs addition
addisoncrump Mar 17, 2022
ed7eae6
fix clippy warnings + errors
addisoncrump Mar 17, 2022
6b52c4d
Merge branch 'main' of github.com:boa-dev/boa into experimental-fuzzer
addisoncrump Mar 17, 2022
e053e29
remove unnecessary excludes
addisoncrump Mar 17, 2022
61855a5
fix clippy, again
addisoncrump Mar 17, 2022
3ece1e4
Make extend_lifetime unsafe + give it explicit lifetimes
addisoncrump Mar 17, 2022
47d484a
whoops, explicit lifetime on replace_inner
addisoncrump Mar 17, 2022
d4bfe84
fix clippy lints
addisoncrump Mar 17, 2022
7826bac
remove irrelevant comment
addisoncrump Mar 17, 2022
ba98b69
Add fuzzing docs
addisoncrump Mar 17, 2022
0910729
typo + link for de-arbitrary
addisoncrump Mar 17, 2022
d18ed44
update command
addisoncrump Mar 17, 2022
a615901
formatter time
addisoncrump Mar 17, 2022
dd85b72
clarify character generation in Name
addisoncrump Mar 17, 2022
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ fuzzer = ["arbitrary", "boa_interner/fuzzer", "num-bigint/arbitrary"]

[dependencies]
arbitrary = { version = "1", features = ["derive"], optional = true }
boa_unicode = { path = "../boa_unicode", version = "0.13.0" }
boa_interner = { path = "../boa_interner", version = "0.13.0" }
boa_gc = { path = "../boa_gc", version = "0.13.0" }
boa_unicode = { path = "../boa_unicode", version = "0.14.0" }
boa_interner = { path = "../boa_interner", version = "0.14.0" }
boa_gc = { path = "../boa_gc", version = "0.14.0" }
gc = { version = "0.4.1" }
boa_profiler = { path = "../boa_profiler", version = "0.14.0" }
serde = { version = "1.0.136", features = ["derive", "rc"] }
Expand Down
1 change: 1 addition & 0 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub struct Context {
/// Whether or not global strict mode is active.
strict: bool,

/// The maximum number of instructions which will be executed in this context.
#[cfg(feature = "fuzzer")]
pub(crate) max_insns: usize,

Expand Down
3 changes: 3 additions & 0 deletions boa_engine/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,6 +1473,9 @@ impl Context {
while self.vm.frame().pc < self.vm.frame().code.code.len() {
#[cfg(feature = "fuzzer")]
{
// update and check how many VM instructions have been executed to make sure we
// don't introduce a spurious timeout in the fuzzer from a source which loops
// infinitely
insns_executed += 1;
if insns_executed > self.max_insns {
return Err("instruction max exceeded".into());
Expand Down
28 changes: 26 additions & 2 deletions boa_inputgen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
[package]
name = "boa_inputgen"
authors = ["Addison Crump <[email protected]>"]
version = "0.1.0"
version = "0.14.0"
edition = "2021"
rust-version = "1.58"
authors = ["Addison Crump <[email protected]>"]
description = "Arbitrary JavaScript input generator used to fuzz Boa."
repository = "https://github.com/boa-dev/boa"
keywords = ["javascript", "js", "fuzzing", "testing"]
categories = ["compilers"]
license = "Unlicense/MIT"
exclude = [
"../.vscode/*",
"../.editorconfig",
"../test262/*",
"../node_modules/*",
"../target/*",
"../dist/*",
"../.github/*",
"../assets/*",
"../docs/*",
"../*.js",
"../test_ignore.txt",
"../yarn.lock",
"../package.json",
"../index.html",
"../tests/*",
"../.github/*",
]
Razican marked this conversation as resolved.
Show resolved Hide resolved

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
88 changes: 88 additions & 0 deletions boa_inputgen/src/data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::collections::HashSet;

use arbitrary::{size_hint, Arbitrary, Unstructured};
use spin::lazy::Lazy;

use boa_engine::syntax::ast::node::StatementList;
use boa_interner::{Interner, ToInternedString};

use crate::replace_syms;

static ALPHA: Lazy<Vec<u8>> = Lazy::new(|| {
let mut all = Vec::new();
all.extend(b'A'..b'Z');
all.extend(b'a'..b'z');
all
});

static ALPHANUM: Lazy<Vec<u8>> = Lazy::new(|| {
let mut all = Vec::new();
all.extend(b'0'..b'9');
all.extend(b'A'..b'Z');
all.extend(b'a'..b'z');
all
});

#[derive(Debug, PartialEq, Eq, Hash)]
struct Name {
name: String,
}

impl<'a> arbitrary::Arbitrary<'a> for Name {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let first = u8::arbitrary(u)?; // at least one
let first = ALPHA[(first as usize) % ALPHA.len()];
let mut chars: Vec<u8> = vec![first];
let mut second: Vec<u8> = Arbitrary::arbitrary(u)?;
second
.iter_mut()
.for_each(|c| *c = ALPHANUM[(*c as usize) % ALPHANUM.len()]);
chars.extend(second);
Ok(Self {
name: String::from_utf8(chars).unwrap(),
})
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
size_hint::and(u8::size_hint(depth), Vec::<u8>::size_hint(depth))
}
}

/// Fuzz data which can be arbitrarily generated and used to test boa's parser, compiler, and vm
#[derive(Debug, Clone)]
pub struct FuzzData {
source: String,
}

impl<'a> Arbitrary<'a> for FuzzData {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let first_name = Name::arbitrary(u)?; // need at least one
let mut vars = HashSet::<Name>::arbitrary(u)?;
vars.insert(first_name);
let mut sample = StatementList::arbitrary(u)?;
let mut interner = Interner::with_capacity(vars.len());
let syms = vars
.into_iter()
.map(|var| interner.get_or_intern(var.name))
.collect::<Vec<_>>();
replace_syms(&syms, &mut sample);
Ok(Self {
source: sample.to_interned_string(&interner),
})
}

fn size_hint(depth: usize) -> (usize, Option<usize>) {
size_hint::and_all(&[
Name::size_hint(depth),
HashSet::<Name>::size_hint(depth),
StatementList::size_hint(depth),
])
}
}

impl FuzzData {
/// Get the source represented by this fuzz data
pub fn get_source(&self) -> &str {
&self.source
}
}
Loading