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

feat(wasm-gen): create config for precise sys-calls #3262

Merged
merged 12 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 11 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
3 changes: 2 additions & 1 deletion utils/node-loader/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use gear_core::ids::{MessageId, ProgramId};
use gear_core_errors::ReplyCode;
use gear_utils::NonEmpty;
use gear_wasm_gen::{
EntryPointsSet, ParamType, StandardGearWasmConfigsBundle, SysCallName,
EntryPointsSet, InvocableSysCall, ParamType, StandardGearWasmConfigsBundle, SysCallName,
SysCallsInjectionAmounts, SysCallsParamsConfig,
};
use gsdk::metadata::runtime_types::{
Expand Down Expand Up @@ -224,6 +224,7 @@ pub fn get_wasm_gen_config(
(SysCallName::Alloc, 5..=10),
(SysCallName::Free, 5..=10),
]
.map(|(sys_call, range)| (InvocableSysCall::Loose(sys_call), range))
.into_iter(),
);

Expand Down
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
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(())
}
3 changes: 2 additions & 1 deletion utils/runtime-fuzzer/src/arbitrary_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use arbitrary::{Arbitrary, Result, Unstructured};
use gear_core::ids::{CodeId, ProgramId};
use gear_utils::NonEmpty;
use gear_wasm_gen::{
EntryPointsSet, ParamType, StandardGearWasmConfigsBundle, SysCallName,
EntryPointsSet, InvocableSysCall, ParamType, StandardGearWasmConfigsBundle, SysCallName,
SysCallsInjectionAmounts, SysCallsParamsConfig,
};
use sha1::*;
Expand Down Expand Up @@ -203,6 +203,7 @@ fn config(
(SysCallName::Alloc, 20..=30),
(SysCallName::Free, 20..=30),
]
.map(|(sys_call, range)| (InvocableSysCall::Loose(sys_call), range))
.into_iter(),
);

Expand Down
8 changes: 4 additions & 4 deletions utils/wasm-gen/src/config/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl SysCallsConfigBuilder {
/// 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.sys_call_destination = SysCallDestination::Source;
self.enable_sys_call(SysCallName::Source);
self.enable_sys_call(InvocableSysCall::Loose(SysCallName::Source));

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 @@ -145,7 +145,7 @@ pub struct SysCallsConfig {

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)
}

Expand Down
34 changes: 21 additions & 13 deletions utils/wasm-gen/src/config/syscalls/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,59 @@
//!
//! Types here are used to create [`crate::SysCallsConfig`].

use crate::InvocableSysCall;

use gear_wasm_instrument::syscalls::SysCallName;
use std::{collections::HashMap, ops::RangeInclusive};

/// Possible injection amount ranges for each sys-call.
#[derive(Debug, Clone)]
pub struct SysCallsInjectionAmounts(HashMap<SysCallName, RangeInclusive<u32>>);
pub struct SysCallsInjectionAmounts(HashMap<InvocableSysCall, RangeInclusive<u32>>);

impl SysCallsInjectionAmounts {
/// Instantiate a sys-calls amounts ranges map, where each gear sys-call is injected into wasm-module only once.
pub fn all_once() -> Self {
Self(
SysCallName::instrumentable()
.into_iter()
.map(|name| (name, (1..=1)))
.collect(),
)
Self::new_with_range(1..=1)
}

/// Instantiate a sys-calls amounts ranges map, where no gear sys-call is ever injected into wasm-module.
pub fn all_never() -> Self {
Self::new_with_range(0..=0)
}

/// Instantiate a sys-calls amounts ranges map with given range.
fn new_with_range(range: RangeInclusive<u32>) -> Self {
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
let sys_calls = SysCallName::instrumentable();
Self(
SysCallName::instrumentable()
.into_iter()
.map(|name| (name, (0..=0)))
sys_calls
.iter()
.cloned()
.map(|name| (InvocableSysCall::Loose(name), range.clone()))
.chain(sys_calls.iter().cloned().filter_map(|name| {
InvocableSysCall::has_precise_variant(name)
.then_some((InvocableSysCall::Precise(name), range.clone()))
}))
.collect(),
)
}

/// Get amount possible sys-call amount range.
pub fn get(&self, name: SysCallName) -> RangeInclusive<u32> {
pub fn get(&self, name: InvocableSysCall) -> RangeInclusive<u32> {
self.0
.get(&name)
.cloned()
.expect("instantiated with all sys-calls set")
}

/// Sets possible amount range for the the sys-call.
pub fn set(&mut self, name: SysCallName, min: u32, max: u32) {
pub fn set(&mut self, name: InvocableSysCall, min: u32, max: u32) {
self.0.insert(name, min..=max);
}

/// Same as [`SysCallsAmountRanges::set`], but sets amount ranges for multiple sys-calls.
pub fn set_multiple(
&mut self,
sys_calls_freqs: impl Iterator<Item = (SysCallName, RangeInclusive<u32>)>,
sys_calls_freqs: impl Iterator<Item = (InvocableSysCall, RangeInclusive<u32>)>,
) {
self.0.extend(sys_calls_freqs)
}
Expand Down
38 changes: 38 additions & 0 deletions utils/wasm-gen/src/generator/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,44 @@ impl InvocableSysCall {
}
}

/// Checks whether given sys-call has the precise variant.
pub(crate) fn has_precise_variant(sys_call: SysCallName) -> bool {
Self::required_imports_for_sys_call(sys_call).is_some()
}

/// Returns the required imports to build precise sys-call.
fn required_imports_for_sys_call(sys_call: SysCallName) -> Option<&'static [SysCallName]> {
// NOTE: the last sys-call must be pattern itself
Some(match sys_call {
SysCallName::ReservationSend => {
&[SysCallName::ReserveGas, SysCallName::ReservationSend]
}
SysCallName::ReservationReply => {
&[SysCallName::ReserveGas, SysCallName::ReservationReply]
}
SysCallName::SendCommit => &[
SysCallName::SendInit,
SysCallName::SendPush,
SysCallName::SendCommit,
],
SysCallName::SendCommitWGas => &[
SysCallName::Size,
SysCallName::SendInit,
SysCallName::SendPushInput,
SysCallName::SendCommitWGas,
],
_ => return None,
})
}

/// Returns the required imports to build precise sys-call, but of a fixed size.
fn required_imports<const N: usize>(sys_call: SysCallName) -> &'static [SysCallName; N] {
Self::required_imports_for_sys_call(sys_call)
.expect("failed to find required imports for sys-call")
.try_into()
.expect("failed to convert slice")
}

// If syscall changes from fallible into infallible or vice versa in future,
// we'll see it by analyzing code coverage stats produced by fuzzer.
pub(crate) fn is_fallible(&self) -> bool {
Expand Down
Loading