Skip to content

Commit

Permalink
feat(runtime-fuzzer): Aggregational PR for different fuzzer features …
Browse files Browse the repository at this point in the history
…and adjustments (#3268)
  • Loading branch information
techraed authored Sep 18, 2023
1 parent 50a4ec1 commit 766c8bc
Show file tree
Hide file tree
Showing 16 changed files with 381 additions and 184 deletions.
2 changes: 1 addition & 1 deletion docker/runtime-fuzzer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ubuntu:22.10
FROM ubuntu:22.04

MAINTAINER GEAR

Expand Down
28 changes: 27 additions & 1 deletion utils/node-loader/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use gear_call_gen::Seed;
use gear_core::ids::{MessageId, ProgramId};
use gear_core_errors::ReplyCode;
use gear_utils::NonEmpty;
use gear_wasm_gen::{EntryPointsSet, StandardGearWasmConfigsBundle};
use gear_wasm_gen::{
EntryPointsSet, InvocableSysCall, ParamType, StandardGearWasmConfigsBundle, SysCallName,
SysCallsInjectionAmounts, SysCallsParamsConfig,
};
use gsdk::metadata::runtime_types::{
gear_common::event::DispatchStatus as GenDispatchStatus,
gear_core::{
Expand Down Expand Up @@ -209,10 +212,33 @@ pub fn get_wasm_gen_config(
seed: Seed,
existing_programs: impl Iterator<Item = ProgramId>,
) -> StandardGearWasmConfigsBundle<ProgramId> {
let initial_pages = 2;
let mut injection_amounts = SysCallsInjectionAmounts::all_once();
injection_amounts.set_multiple(
[
(SysCallName::Leave, 0..=0),
(SysCallName::Panic, 0..=0),
(SysCallName::OomPanic, 0..=0),
(SysCallName::Send, 20..=30),
(SysCallName::Exit, 0..=1),
(SysCallName::Alloc, 5..=10),
(SysCallName::Free, 5..=10),
]
.map(|(sys_call, range)| (InvocableSysCall::Loose(sys_call), range))
.into_iter(),
);

let mut params_config = SysCallsParamsConfig::default();
params_config.add_rule(ParamType::Alloc, (1..=10).into());
params_config.add_rule(ParamType::Free, (initial_pages..=initial_pages + 25).into());

StandardGearWasmConfigsBundle {
log_info: Some(format!("Gear program seed = '{seed}'")),
existing_addresses: NonEmpty::collect(existing_programs),
entry_points_set: EntryPointsSet::InitHandleHandleReply,
injection_amounts,
params_config,
initial_pages: initial_pages as u32,
..Default::default()
}
}
4 changes: 4 additions & 0 deletions utils/runtime-fuzzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ version = "0.1.0"
authors.workspace = true
edition.workspace = true

[[bin]]
name = "run_corpus"
path = "bin/run_corpus.rs"

[dependencies]
anyhow.workspace = true
arbitrary.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion utils/runtime-fuzzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ There are two ways to view coverage:
```bash
# generate `lcov.info` file with coverage
HOST_TARGET=$(rustc -Vv | grep "host: " | sed "s/^host: \(.*\)$/\1/")
cargo cov -- export target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/main \
cargo cov -- export target/$HOST_TARGET/coverage/$HOST_TARGET/release/main \
--format=lcov \
--instr-profile=fuzz/coverage/main/coverage.profdata \
--ignore-filename-regex=/rustc/ \
Expand Down
58 changes: 58 additions & 0 deletions utils/runtime-fuzzer/bin/run_corpus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// This file is part of Gear.

// Copyright (C) 2021-2023 Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Runs provided from the cli corpus
//!
//! Alternatively, `cargo fuzz run` can be used to reproduce some corpus,
//! but it won't give logs of [`GearCalls`] generation, which sheds some
//! light on how `gear-wasm-gen` worked.
//!
//! Also that script can be used to run any bytes input, not only fuzzer's
//! corpus.
//!
//! Just simply run `cargo run --release -- -p <path_to_corpus>`.
use anyhow::Result;
use arbitrary::{Arbitrary, Unstructured};
use clap::Parser;
use runtime_fuzzer::{self, GearCalls};
use std::{fs, path::PathBuf};

/// A simple tool to run corpus.
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
struct Params {
/// Path to the file, which contains corpus.
#[arg(short, long)]
path: PathBuf,
}

fn main() -> Result<()> {
let params = Params::parse();

let corpus_bytes = fs::read(params.path)?;

gear_utils::init_default_logger();

let mut unstructured = Unstructured::new(&corpus_bytes);
let gear_calls = GearCalls::arbitrary(&mut unstructured)?;

runtime_fuzzer::run(gear_calls);

Ok(())
}
31 changes: 25 additions & 6 deletions utils/runtime-fuzzer/src/arbitrary_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ use arbitrary::{Arbitrary, Result, Unstructured};
use gear_core::ids::{CodeId, ProgramId};
use gear_utils::NonEmpty;
use gear_wasm_gen::{
EntryPointsSet, StandardGearWasmConfigsBundle, SysCallName, SysCallsInjectionAmounts,
EntryPointsSet, InvocableSysCall, ParamType, StandardGearWasmConfigsBundle, SysCallName,
SysCallsInjectionAmounts, SysCallsParamsConfig,
};
use sha1::*;
use std::{
Expand Down Expand Up @@ -190,12 +191,28 @@ fn config(
programs: [ProgramId; GearCalls::INIT_MSGS],
log_info: Option<String>,
) -> StandardGearWasmConfigsBundle<ProgramId> {
let initial_pages = 2;
let mut injection_amounts = SysCallsInjectionAmounts::all_once();
injection_amounts.set(SysCallName::Leave, 0, 0);
injection_amounts.set(SysCallName::Panic, 0, 0);
injection_amounts.set(SysCallName::OomPanic, 0, 0);
injection_amounts.set(SysCallName::Send, 20, 30);
injection_amounts.set(SysCallName::Exit, 0, 1);
injection_amounts.set_multiple(
[
(SysCallName::Leave, 0..=0),
(SysCallName::Panic, 0..=0),
(SysCallName::OomPanic, 0..=0),
(SysCallName::Send, 20..=30),
(SysCallName::Exit, 0..=1),
(SysCallName::Alloc, 20..=30),
(SysCallName::Free, 20..=30),
]
.map(|(sys_call, range)| (InvocableSysCall::Loose(sys_call), range))
.into_iter(),
);

let mut params_config = SysCallsParamsConfig::default();
params_config.add_rule(ParamType::Alloc, (10..=20).into());
params_config.add_rule(
ParamType::Free,
(initial_pages..=initial_pages + 250).into(),
);

let existing_addresses = NonEmpty::collect(
programs
Expand All @@ -214,6 +231,8 @@ fn config(
injection_amounts,
existing_addresses,
log_info,
params_config,
initial_pages: initial_pages as u32,
..Default::default()
}
}
Expand Down
20 changes: 20 additions & 0 deletions utils/wasm-gen/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ pub struct StandardGearWasmConfigsBundle<T = [u8; 32]> {
pub injection_amounts: SysCallsInjectionAmounts,
/// Config of gear wasm call entry-points (exports).
pub entry_points_set: EntryPointsSet,
/// Initial wasm memory pages.
pub initial_pages: u32,
/// Optional stack end pages.
pub stack_end_page: Option<u32>,
/// Sys-calls params config
pub params_config: SysCallsParamsConfig,
}

impl<T> Default for StandardGearWasmConfigsBundle<T> {
Expand All @@ -158,6 +164,9 @@ impl<T> Default for StandardGearWasmConfigsBundle<T> {
call_indirect_enabled: true,
injection_amounts: SysCallsInjectionAmounts::all_once(),
entry_points_set: Default::default(),
initial_pages: DEFAULT_INITIAL_SIZE,
stack_end_page: None,
params_config: SysCallsParamsConfig::default(),
}
}
}
Expand All @@ -171,6 +180,9 @@ impl<T: Into<Hash>> ConfigsBundle for StandardGearWasmConfigsBundle<T> {
call_indirect_enabled,
injection_amounts,
entry_points_set,
initial_pages,
stack_end_page,
params_config,
} = self;

let selectable_params = SelectableParams {
Expand All @@ -188,10 +200,18 @@ impl<T: Into<Hash>> ConfigsBundle for StandardGearWasmConfigsBundle<T> {
} else {
sys_calls_config_builder = sys_calls_config_builder.with_source_msg_dest();
}
sys_calls_config_builder = sys_calls_config_builder.with_params_config(params_config);

let memory_pages_config = MemoryPagesConfig {
initial_size: initial_pages,
stack_end_page,
upper_limit: None,
};
let gear_wasm_generator_config = GearWasmGeneratorConfigBuilder::new()
.with_recursions_removed(remove_recursion)
.with_sys_calls_config(sys_calls_config_builder.build())
.with_entry_points_config(entry_points_set)
.with_memory_config(memory_pages_config)
.build();

(gear_wasm_generator_config, selectable_params)
Expand Down
6 changes: 4 additions & 2 deletions utils/wasm-gen/src/config/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
use crate::SysCallsConfig;

pub(crate) const DEFAULT_INITIAL_SIZE: u32 = 16;

/// Builder for [`GearWasmGeneratorConfig`].
pub struct GearWasmGeneratorConfigBuilder(GearWasmGeneratorConfig);

Expand Down Expand Up @@ -95,9 +97,9 @@ pub struct MemoryPagesConfig {
impl Default for MemoryPagesConfig {
fn default() -> Self {
Self {
initial_size: Self::MAX_VALUE / 2 + 5,
initial_size: DEFAULT_INITIAL_SIZE,
upper_limit: None,
stack_end_page: Some(Self::MAX_VALUE / 2),
stack_end_page: None,
}
}
}
Expand Down
50 changes: 25 additions & 25 deletions utils/wasm-gen/src/config/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl SysCallsConfigBuilder {
Self(SysCallsConfig {
injection_amounts,
params_config: SysCallsParamsConfig::default(),
sending_message_destination: MessageDestination::default(),
sys_call_destination: SysCallDestination::default(),
error_processing_config: ErrorProcessingConfig::None,
log_info: None,
})
Expand All @@ -54,23 +54,23 @@ impl SysCallsConfigBuilder {
self
}

/// Set whether `gr_send*` sys-calls must use `gr_source` result for message destination.
/// Set whether `gr_send*` and `gr_exit` sys-calls must use `gr_source` result for sys-call destination.
pub fn with_source_msg_dest(mut self) -> Self {
self.0.sending_message_destination = MessageDestination::Source;
self.enable_sys_call(SysCallName::Source);
self.0.sys_call_destination = SysCallDestination::Source;
self.enable_sys_call(InvocableSysCall::Loose(SysCallName::Source));

self
}

/// Set whether `gr_send*` sys-calls must use some address from `addresses` collection
/// as a message destination.
/// Set whether `gr_send*` and `gr_exit` sys-calls must use some address from `addresses` collection
/// as a sys-call destination.
pub fn with_data_offset_msg_dest<T: Into<Hash>>(mut self, addresses: NonEmpty<T>) -> Self {
let addresses = NonEmpty::collect(addresses.into_iter().map(|pid| HashWithValue {
hash: pid.into(),
value: 0,
}))
.expect("collected from non empty");
self.0.sending_message_destination = MessageDestination::ExistingAddresses(addresses);
self.0.sys_call_destination = SysCallDestination::ExistingAddresses(addresses);

self
}
Expand All @@ -81,7 +81,7 @@ impl SysCallsConfigBuilder {
/// Choosing gear export to log data is done from best `init` to worse `handle`.
pub fn with_log_info(mut self, log: String) -> Self {
self.0.log_info = Some(log);
self.enable_sys_call(SysCallName::Debug);
self.enable_sys_call(InvocableSysCall::Loose(SysCallName::Debug));

self
}
Expand All @@ -93,7 +93,7 @@ impl SysCallsConfigBuilder {
self
}

fn enable_sys_call(&mut self, name: SysCallName) {
fn enable_sys_call(&mut self, name: InvocableSysCall) {
let range = self.0.injection_amounts.get(name);

let range_start = *range.start();
Expand Down Expand Up @@ -138,22 +138,22 @@ impl ErrorProcessingConfig {
pub struct SysCallsConfig {
injection_amounts: SysCallsInjectionAmounts,
params_config: SysCallsParamsConfig,
sending_message_destination: MessageDestination,
sys_call_destination: SysCallDestination,
error_processing_config: ErrorProcessingConfig,
log_info: Option<String>,
}

impl SysCallsConfig {
/// Get possible number of times (range) the sys-call can be injected in the wasm.
pub fn injection_amounts(&self, name: SysCallName) -> RangeInclusive<u32> {
pub fn injection_amounts(&self, name: InvocableSysCall) -> RangeInclusive<u32> {
self.injection_amounts.get(name)
}

/// Get defined message destination for `gr_send*` sys-calls.
/// Get defined sys-call destination for `gr_send*` and `gr_exit` sys-calls.
///
/// For more info, read [`MessageDestination`].
pub fn sending_message_destination(&self) -> &MessageDestination {
&self.sending_message_destination
/// For more info, read [`SysCallDestination`].
pub fn sys_call_destination(&self) -> &SysCallDestination {
&self.sys_call_destination
}

/// Get defined log info.
Expand All @@ -174,33 +174,33 @@ impl SysCallsConfig {
}
}

/// Message destination choice.
/// Sys-call destination choice.
///
/// `gr_send*` sys-calls generated from this crate can send messages
/// `gr_send*` and `gr_exit` sys-calls generated from this crate can be sent
/// to different destination in accordance to the config.
/// It's either to the message source, to some existing known address,
/// or to some random, most probably non-existing, address.
#[derive(Debug, Clone, Default)]
pub enum MessageDestination {
pub enum SysCallDestination {
Source,
ExistingAddresses(NonEmpty<HashWithValue>),
#[default]
Random,
}

impl MessageDestination {
/// Check whether message destination is a result of `gr_source`.
impl SysCallDestination {
/// Check whether sys-call destination is a result of `gr_source`.
pub fn is_source(&self) -> bool {
matches!(&self, MessageDestination::Source)
matches!(&self, SysCallDestination::Source)
}

/// Check whether message destination is defined randomly.
/// Check whether sys-call destination is defined randomly.
pub fn is_random(&self) -> bool {
matches!(&self, MessageDestination::Random)
matches!(&self, SysCallDestination::Random)
}

/// Check whether message destination is defined from a collection of existing addresses.
/// Check whether sys-call destination is defined from a collection of existing addresses.
pub fn is_existing_addresses(&self) -> bool {
matches!(&self, MessageDestination::ExistingAddresses(_))
matches!(&self, SysCallDestination::ExistingAddresses(_))
}
}
Loading

0 comments on commit 766c8bc

Please sign in to comment.