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

Cairo Native fuzzer #46

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions cairo-native-fuzzer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
/cairo2
/corelib
cairo*.tar
23 changes: 23 additions & 0 deletions cairo-native-fuzzer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "cairo-native-fuzzer"
version = "0.1.0"
edition = "2021"

[dependencies]
cairo-lang-compiler = "2.8.4"
cairo-lang-sierra = "2.8.4"
cairo-lang-starknet = "2.8.4"
cairo-native = { version = "0.2.0-alpha.4", features = ["with-runtime"] }
cairo-native-runtime = { version = "0.2.0-alpha.4", optional = true }
clap = "4.5.20"
rand = "0.8.5"
starknet-types-core = "0.1.7"

[dependencies.felt]
git = 'https://github.com/FuzzingLabs/cairo-rs'
rev = '48af153240392992f18a09e969bae6518eec9639'
package = 'cairo-felt'

[features]
default = ["with-runtime"]
with-runtime = ["dep:cairo-native-runtime"]
15 changes: 15 additions & 0 deletions cairo-native-fuzzer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Cairo Native Fuzzer

Cairo Native Fuzzer is a rewrite of the Cairo Fuzzer based on [Cairo native from Lambdaclass](https://github.com/lambdaclass/cairo_native) developed to enhance fuzzer execution speed.

### Roadmap

### Step 1 : Create a basic fuzzer based on Cairo Native :
- [x] Implement the Cairo Native runner
- [x] Implement the fuzzer based on Cairo Native runner
- [x] Import existing mutator from the cairo-fuzzer

### Step 2 : Integrate existing cairo-fuzzer features into Cairo Native fuzzer :
- [ ] Multithreading
- [ ] Support config files
- [ ] Property testing
56 changes: 56 additions & 0 deletions cairo-native-fuzzer/examples/fuzzinglabs.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use starknet::{
Store, SyscallResult, StorageBaseAddress, storage_read_syscall, storage_write_syscall,
storage_address_from_base_and_offset
};
use integer::{
U128IntoFelt252, Felt252IntoU256, Felt252TryIntoU64, U256TryIntoFelt252, u256_from_felt252
};


#[starknet::contract]
mod test_contract {
#[storage]
struct Storage {
bal:u8
}
#[external(v0)]
fn Fuzz_symbolic_execution(
ref self: ContractState,
f: felt252,
u: felt252,
z: u16,
z2: u32,
i: u64,
n: u128,
g: u128,
l: u128,
a: felt252,
b: felt252,
s: u8,
) {
if (f == 'f') {
if (u == 'u') {
if (z == 'z') {
if (z2 == 'z') {
if (i == 'i') {
if (n == 'n') {
if (g == 'g') {
if (l == 'l') {
if (a == 'a') {
if (b == 'b') {
if (s == 's') {
assert(1==0 , '!(f & t)');
}
}
}
}
}
}
}
}
}
}
}
return ();
}
}
164 changes: 164 additions & 0 deletions cairo-native-fuzzer/src/fuzzer/fuzzer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use std::path::PathBuf;
use std::sync::Arc;

use cairo_lang_compiler::CompilerConfig;
use cairo_lang_sierra::ids::FunctionId;
use cairo_lang_sierra::program::Program;
use cairo_lang_starknet::compile::compile_path;
use starknet_types_core::felt::Felt;

use crate::mutator::argument_type::map_argument_type;
use crate::mutator::argument_type::ArgumentType;
use crate::mutator::basic_mutator::Mutator;
use crate::runner::runner::CairoNativeRunner;
use crate::utils::get_function_by_id;

#[allow(dead_code)]
pub struct Fuzzer {
program_path: PathBuf,
entry_point: String,
runner: CairoNativeRunner,
sierra_program: Option<Arc<Program>>,
params: Vec<Felt>,
entry_point_id: Option<FunctionId>,
mutator: Mutator,
argument_types: Vec<ArgumentType>,
}

impl Fuzzer {
pub fn new(program_path: PathBuf, entry_point: String) -> Self {
Self {
program_path,
entry_point,
runner: CairoNativeRunner::new(),
sierra_program: None,
params: Vec::new(),
entry_point_id: None,
mutator: Mutator::new(),
argument_types: Vec::new(),
}
}

/// Init the fuzzer
/// - Compile Cairo code to Sierra
/// - Find the entry id
/// - Init the runner
pub fn init(&mut self) -> Result<(), String> {
self.convert_and_store_cairo_to_sierra()?;
self.entry_point_id = Some(self.find_entry_point_id());
self.runner
.init(&self.entry_point_id, &self.sierra_program)?;
self.argument_types = self.get_function_arguments_types();
self.generate_params();
Ok(())
}

/// Compile the Cairo program to Sierra
fn convert_and_store_cairo_to_sierra(&mut self) -> Result<(), String> {
let contract = compile_path(
&self.program_path,
None,
CompilerConfig {
replace_ids: true,
..Default::default()
},
)
.map_err(|e| format!("Failed to compile Cairo program: {}", e))?;

let sierra_program = contract
.extract_sierra_program()
.map_err(|e| format!("Failed to extract Sierra program: {}", e))?;
self.sierra_program = Some(Arc::new(sierra_program));
Ok(())
}

/// Find the entry point id
fn find_entry_point_id(&self) -> FunctionId {
let sierra_program = self
.sierra_program
.as_ref()
.expect("Sierra program not available");
cairo_native::utils::find_function_id(sierra_program, &self.entry_point)
.expect(&format!("Entry point '{}' not found", self.entry_point))
.clone()
}

/// Returns a vector of the function parameter types
///
/// For example, given a function with the prototype:
/// ```
/// myfunction(a: felt252, b: felt252) -> felt252
/// ```
/// This function will return:
/// ```
/// [Felt, Felt]
/// ```
pub fn get_function_arguments_types(&self) -> Vec<ArgumentType> {
let func = match (&self.sierra_program, &self.entry_point_id) {
(Some(program), Some(entry_point_id)) => get_function_by_id(program, entry_point_id),
_ => None,
};

if let Some(func) = func {
let argument_types: Vec<ArgumentType> = func
.signature
.param_types
.iter()
.filter_map(|param_type| {
if let Some(debug_name) = &param_type.debug_name {
// Map param_type to an `ArgumentType`
// For now we only handle felt252
return map_argument_type(debug_name);
}
None
})
.collect();
argument_types
} else {
Vec::new()
}
}

/// Generate params based on the function argument types
pub fn generate_params(&mut self) {
self.params = self
.argument_types
.iter()
.map(|arg_type| match arg_type {
ArgumentType::Felt => Felt::from(0),
// TODO: Add support for other types
})
.collect();
}

/// Mutate a single function parameter
pub fn mutate_param(&mut self, value: Felt) -> Felt {
// Use the Mutator to mutate the felt value
self.mutator.mutate(value)
}

/// Mutate the parameters using the Mutator
fn mutate_params(&mut self) {
// Iterate through the current params and mutate each one
for i in 0..self.params.len() {
let mutated_value = self.mutate_param(self.params[i]);
self.params[i] = mutated_value;
}
}

/// Run the fuzzer
/// We just use an infinite loop for now
pub fn fuzz(&mut self) -> Result<(), String> {
self.generate_params();
loop {
match self.runner.run_program(&self.params) {
Ok(result) => {
println!("Cairo program was compiled and executed successfully.");
println!("{:?}", result);
}
Err(e) => eprintln!("Error during execution: {}", e),
}
self.mutate_params();
}
}
}
1 change: 1 addition & 0 deletions cairo-native-fuzzer/src/fuzzer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod fuzzer;
36 changes: 36 additions & 0 deletions cairo-native-fuzzer/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
mod fuzzer;
mod mutator;
mod runner;
mod utils;

use clap::Parser;
use std::path::PathBuf;

use crate::fuzzer::fuzzer::Fuzzer;

/// Command-line arguments for the fuzzer
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Path to the Cairo program
#[arg(short, long)]
program_path: PathBuf,

/// Entry point of the Sierra program
#[arg(short, long)]
entry_point: String,
}

fn main() {
let args = Args::parse();

let mut fuzzer = Fuzzer::new(args.program_path, args.entry_point);

match fuzzer.init() {
Ok(()) => match fuzzer.fuzz() {
Ok(()) => println!("Fuzzing completed successfully."),
Err(e) => eprintln!("Error during fuzzing: {}", e),
},
Err(e) => eprintln!("Error during initialization: {}", e),
}
}
16 changes: 16 additions & 0 deletions cairo-native-fuzzer/src/mutator/argument_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// Enum representing the types of arguments that can be passed to a function
#[derive(Debug)]
pub enum ArgumentType {
Felt,
// TODO: Add support for other types
}

/// Helper function to map argument types based on their debug names
/// This function takes a debug name string and returns the corresponding `ArgumentType`
pub fn map_argument_type(debug_name: &str) -> Option<ArgumentType> {
match debug_name {
"felt252" => Some(ArgumentType::Felt),
// TODO: Add support for other types
_ => None,
}
}
Loading
Loading