feat: add dependency

This commit is contained in:
2023-01-20 22:36:19 +08:00
parent 68e8d103b4
commit cf8e579f27
644 changed files with 150099 additions and 14 deletions

View File

@@ -0,0 +1,25 @@
#![no_main]
mod common;
use crate::common::FuzzSource;
use boa_engine::Context;
use boa_parser::Parser;
use libfuzzer_sys::{fuzz_target, Corpus};
use std::io::Cursor;
fn do_fuzz(original: FuzzSource) -> Corpus {
let mut ctx = Context::builder()
.interner(original.interner)
.instructions_remaining(0)
.build();
let mut parser = Parser::new(Cursor::new(&original.source));
if let Ok(parsed) = parser.parse_all(ctx.interner_mut()) {
let _ = ctx.compile(&parsed);
Corpus::Keep
} else {
Corpus::Reject
}
}
fuzz_target!(|original: FuzzSource| -> Corpus { do_fuzz(original) });

View File

@@ -0,0 +1,96 @@
use boa_ast::{
visitor::{VisitWith, VisitorMut},
Expression, StatementList,
};
use boa_interner::{Interner, Sym, ToInternedString};
use libfuzzer_sys::arbitrary;
use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};
use std::fmt::{Debug, Formatter};
use std::ops::ControlFlow;
/// Context for performing fuzzing. This structure contains both the generated AST as well as the
/// context used to resolve the symbols therein.
pub struct FuzzData {
pub interner: Interner,
pub ast: StatementList,
}
impl<'a> Arbitrary<'a> for FuzzData {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let mut interner = Interner::with_capacity(8);
let mut syms_available = Vec::with_capacity(8);
for c in 'a'..='h' {
syms_available.push(interner.get_or_intern(&*String::from(c)));
}
let mut ast = StatementList::arbitrary(u)?;
struct FuzzReplacer<'a, 's, 'u> {
syms: &'s [Sym],
u: &'u mut Unstructured<'a>,
}
impl<'a, 's, 'u, 'ast> VisitorMut<'ast> for FuzzReplacer<'a, 's, 'u> {
type BreakTy = arbitrary::Error;
// TODO arbitrary strings literals?
fn visit_expression_mut(
&mut self,
node: &'ast mut Expression,
) -> ControlFlow<Self::BreakTy> {
if matches!(node, Expression::FormalParameterList(_)) {
match self.u.arbitrary() {
Ok(id) => *node = Expression::Identifier(id),
Err(e) => return ControlFlow::Break(e),
}
}
node.visit_with_mut(self)
}
fn visit_sym_mut(&mut self, node: &'ast mut Sym) -> ControlFlow<Self::BreakTy> {
*node = self.syms[node.get() % self.syms.len()];
ControlFlow::Continue(())
}
}
let mut replacer = FuzzReplacer {
syms: &syms_available,
u,
};
if let ControlFlow::Break(e) = replacer.visit_statement_list_mut(&mut ast) {
Err(e)
} else {
Ok(Self { interner, ast })
}
}
}
impl Debug for FuzzData {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FuzzData")
.field("ast", &self.ast)
.finish_non_exhaustive()
}
}
pub struct FuzzSource {
pub interner: Interner,
pub source: String,
}
impl<'a> Arbitrary<'a> for FuzzSource {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let data = FuzzData::arbitrary(u)?;
let source = data.ast.to_interned_string(&data.interner);
Ok(Self {
interner: data.interner,
source,
})
}
}
impl Debug for FuzzSource {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("Fuzzed source:\n{}", self.source))
}
}

View File

@@ -0,0 +1,74 @@
#![no_main]
mod common;
use crate::common::FuzzData;
use boa_interner::ToInternedString;
use boa_parser::Parser;
use libfuzzer_sys::fuzz_target;
use libfuzzer_sys::Corpus;
use std::error::Error;
use std::io::Cursor;
/// Fuzzer test harness. This function accepts the arbitrary AST and performs the fuzzing operation.
///
/// See [README.md](../README.md) for details on the design of this fuzzer.
fn do_fuzz(mut data: FuzzData) -> Result<(), Box<dyn Error>> {
let original = data.ast.to_interned_string(&data.interner);
let mut parser = Parser::new(Cursor::new(&original));
let before = data.interner.len();
// For a variety of reasons, we may not actually produce valid code here (e.g., nameless function).
// Fail fast and only make the next checks if we were valid.
if let Ok(first) = parser.parse_all(&mut data.interner) {
let after_first = data.interner.len();
let first_interned = first.to_interned_string(&data.interner);
assert_eq!(
before,
after_first,
"The number of interned symbols changed; a new string was read.\nBefore:\n{}\nAfter:\n{}\nBefore (AST):\n{:#?}\nAfter (AST):\n{:#?}",
original,
first_interned,
data.ast,
first
);
let mut parser = Parser::new(Cursor::new(&first_interned));
// Now, we most assuredly should produce valid code. It has already gone through a first pass.
let second = parser
.parse_all(&mut data.interner)
.expect("Could not parse the first-pass interned copy.");
let second_interned = second.to_interned_string(&data.interner);
let after_second = data.interner.len();
assert_eq!(
after_first,
after_second,
"The number of interned symbols changed; a new string was read.\nBefore:\n{}\nAfter:\n{}\nBefore (AST):\n{:#?}\nAfter (AST):\n{:#?}",
first_interned,
second_interned,
first,
second
);
assert_eq!(
first,
second,
"Expected the same AST after two intern passes, but found dissimilar.\nOriginal:\n{}\nFirst:\n{}\nSecond:\n{}",
original,
first_interned,
second_interned,
);
}
Ok(())
}
// Fuzz harness wrapper to expose it to libfuzzer (and thus cargo-fuzz)
// See: https://rust-fuzz.github.io/book/cargo-fuzz.html
fuzz_target!(|data: FuzzData| -> Corpus {
if do_fuzz(data).is_ok() {
Corpus::Keep
} else {
Corpus::Reject
}
});

View File

@@ -0,0 +1,19 @@
#![no_main]
mod common;
use crate::common::FuzzSource;
use boa_engine::{Context, JsResult, JsValue};
use libfuzzer_sys::fuzz_target;
fn do_fuzz(original: FuzzSource) -> JsResult<JsValue> {
let mut ctx = Context::builder()
.interner(original.interner)
.instructions_remaining(1 << 16)
.build();
ctx.eval(&original.source)
}
fuzz_target!(|original: FuzzSource| {
let _ = do_fuzz(original);
});